From e535b417d7e889be139144712a7170ca35acf400 Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Mon, 11 Sep 2023 15:38:55 +0200 Subject: [PATCH] Any fixes --- .composer-require-checker.json | 8 +- .github/workflows/diagnostics.yml | 2 +- .phpcq.yaml.dist | 1 + composer.json | 2 +- src/Clipboard/ClipboardInterface.php | 2 +- src/Clipboard/Filter.php | 70 +-- .../Callback/ModelLabelCallbackListener.php | 7 +- ...rtyInputFieldGetWizardCallbackListener.php | 11 +- ...rtyInputFieldGetXLabelCallbackListener.php | 11 +- src/Contao/Compatibility/DcCompat.php | 7 +- .../Legacy/LegacyDcaDataDefinitionBuilder.php | 6 +- .../Dca/Populator/BackendViewPopulator.php | 4 + .../Dca/Populator/HardCodedPopulator.php | 13 +- src/Contao/Factory/SessionStorageFactory.php | 7 +- src/Contao/Picker/PagePickerProvider.php | 37 +- src/Contao/SessionStorage.php | 40 +- .../DynamicParentTableSubscriber.php | 14 +- .../Subscriber/FallbackResetSubscriber.php | 53 +- .../Subscriber/FormatModelLabelSubscriber.php | 31 +- .../AbstractListShowAllHandler.php | 249 ++++++--- .../ActionHandler/CopyHandler.php | 112 +++- .../ActionHandler/CreateHandler.php | 38 +- .../ActionHandler/DeleteHandler.php | 79 ++- .../ActionHandler/EditHandler.php | 48 +- .../ActionHandler/ListViewShowAllHandler.php | 20 +- .../MultipleHandler/EditAllHandler.php | 74 ++- .../MultipleHandler/OverrideAllHandler.php | 94 +++- .../MultipleHandler/PasteAllHandler.php | 108 ++-- .../MultipleHandler/SelectModelAllHandler.php | 10 +- .../SelectPropertyAllHandler.php | 110 ++-- .../ParentedListViewShowAllHandler.php | 195 +++++-- .../ActionHandler/PasteHandler.php | 44 +- .../ActionHandler/SelectHandler.php | 311 ++++++----- .../ActionHandler/ShowHandler.php | 86 ++- .../ActionHandler/ToggleHandler.php | 59 +- .../View/Contao2BackendView/BaseView.php | 210 +++++-- .../Contao2BackendView/ButtonRenderer.php | 165 ++++-- .../ContaoBackendViewTemplate.php | 70 ++- .../ContaoWidgetManager.php | 127 +++-- .../Controller/ClipboardController.php | 97 +++- .../View/Contao2BackendView/EditMask.php | 325 +++++++---- .../Event/BaseButtonEvent.php | 21 +- .../Event/BaseGetButtonsEvent.php | 9 +- .../Event/BuildWidgetEvent.php | 11 +- .../DecodePropertyValueForWidgetEvent.php | 11 +- .../EncodePropertyValueFromWidgetEvent.php | 7 +- .../Event/GetBreadcrumbEvent.php | 7 +- .../Event/GetGlobalButtonEvent.php | 11 +- .../Event/GetGroupHeaderEvent.php | 6 +- .../Event/GetOperationButtonEvent.php | 4 +- .../Event/GetParentHeaderEvent.php | 7 +- .../Event/GetPasteButtonEvent.php | 7 +- .../Event/GetPasteRootButtonEvent.php | 9 +- .../Event/ModelToLabelEvent.php | 8 +- .../Event/ParentViewChildRecordEvent.php | 2 +- .../PrepareMultipleModelsActionEvent.php | 18 +- .../EventListener/BackButtonListener.php | 19 +- .../ColorPickerWizardListener.php | 16 +- .../CreateModelButtonListener.php | 36 +- .../CreateSubHeadlineListener.php | 14 +- .../SelectModeButtonsListener.php | 18 +- .../View/Contao2BackendView/FileSelect.php | 141 +++-- .../Filter/LanguageFilter.php | 70 ++- .../GlobalButtonRenderer.php | 45 +- .../View/Contao2BackendView/PanelBuilder.php | 22 +- .../View/Contao2BackendView/PanelRenderer.php | 31 +- .../View/Contao2BackendView/ParentView.php | 11 +- .../Subscriber/CheckPermission.php | 19 +- .../Subscriber/GetGroupHeaderSubscriber.php | 46 +- .../Subscriber/MultipleHandlerSubscriber.php | 95 +++- .../Subscriber/RichTextFileUuidSubscriber.php | 18 +- .../Subscriber/WidgetBuilder.php | 121 ++-- .../View/Contao2BackendView/TreePicker.php | 515 +++++++++++++----- .../View/Contao2BackendView/TreeSelect.php | 48 +- .../View/Contao2BackendView/TreeView.php | 229 ++++++-- .../View/Contao2BackendView/ViewHelpers.php | 54 +- .../Widget/AbstractWidget.php | 10 +- .../Contao2BackendView/Widget/FileTree.php | 156 ++++-- .../Widget/FileTreeOrder.php | 13 +- .../Contao2BackendView/Widget/PageTree.php | 30 +- .../Widget/PageTreeOrder.php | 2 + .../Widget/TreePickerOrder.php | 6 +- src/ContaoManager/Plugin.php | 7 +- src/Controller/Ajax.php | 72 ++- src/Controller/Ajax3X.php | 166 ++++-- src/Controller/BackendTreeController.php | 223 +++++--- src/Controller/ControllerInterface.php | 38 +- src/Controller/DefaultController.php | 380 +++++++++---- src/Controller/ModelCollector.php | 160 ++++-- src/Controller/SortingManager.php | 107 ++-- src/Controller/TreeCollector.php | 109 ++-- src/Controller/TreeNodeStates.php | 17 +- src/DC/General.php | 48 +- src/Data/AbstractModel.php | 15 +- src/Data/ConfigInterface.php | 6 +- src/Data/DataProviderInterface.php | 4 +- src/Data/DefaultCollection.php | 21 +- src/Data/DefaultConfig.php | 87 +-- src/Data/DefaultDataProvider.php | 93 ++-- src/Data/DefaultDataProviderDBalUtils.php | 130 +++-- src/Data/DefaultDataProviderSqlUtils.php | 24 +- src/Data/DefaultEditInformation.php | 27 +- src/Data/DefaultFilterOptionCollection.php | 4 +- src/Data/DefaultLanguageInformation.php | 6 +- .../DefaultLanguageInformationCollection.php | 2 +- src/Data/DefaultModel.php | 45 +- src/Data/EditInformationInterface.php | 4 +- ...LanguageInformationCollectionInterface.php | 7 +- src/Data/LanguageInformationInterface.php | 2 +- src/Data/ModelId.php | 1 + src/Data/ModelInterface.php | 23 +- src/Data/MultiLanguageDataProvider.php | 2 + .../MultiLanguageDataProviderInterface.php | 9 +- src/Data/NoOpDataProvider.php | 7 +- src/Data/PropertyValueBag.php | 22 +- src/Data/PropertyValueBagInterface.php | 13 +- src/Data/TableRowsAsRecordsDataProvider.php | 44 +- src/DataDefinition/AbstractConditionChain.php | 2 +- ...stractEventDrivenDataDefinitionBuilder.php | 19 +- .../DataProviderInformation.php | 4 +- src/DataDefinition/DefaultContainer.php | 39 +- .../Definition/BasicDefinitionInterface.php | 2 +- .../DataProviderDefinitionInterface.php | 8 +- .../Definition/DefaultBasicDefinition.php | 6 +- .../DefaultDataProviderDefinition.php | 22 +- .../DefaultModelRelationshipDefinition.php | 9 +- .../DefaultPropertiesDefinition.php | 2 +- .../Definition/Properties/DefaultProperty.php | 48 +- .../PropertiesDefinitionInterface.php | 7 +- .../Definition/View/Command.php | 19 +- .../Definition/View/CommandCollection.php | 6 +- .../View/CommandCollectionInterface.php | 2 +- .../Definition/View/CommandInterface.php | 4 +- ...ultGroupAndSortingDefinitionCollection.php | 2 + .../DefaultGroupAndSortingInformation.php | 4 +- .../Definition/View/DefaultListingConfig.php | 8 +- .../View/DefaultModelFormatterConfig.php | 11 +- .../Definition/View/DefaultPanelRow.php | 21 +- .../View/DefaultPanelRowCollection.php | 2 +- .../View/ListingConfigInterface.php | 4 +- .../Panel/DefaultFilterElementInformation.php | 9 +- .../Panel/DefaultSearchElementInformation.php | 9 +- .../View/PanelRowCollectionInterface.php | 9 +- .../Definition/View/PanelRowInterface.php | 9 +- .../Definition/View/ToggleCommand.php | 10 +- .../View/TranslatedToggleCommand.php | 7 +- .../ModelRelationship/FilterBuilder.php | 11 +- .../FilterBuilder/AndFilterBuilder.php | 9 +- .../BaseComparingFilterBuilder.php | 18 +- .../FilterBuilder/BaseFilterBuilder.php | 51 +- .../FilterBuilderWithChildren.php | 60 +- .../FilterBuilder/OrFilterBuilder.php | 7 +- .../PropertyValueInFilterBuilder.php | 9 +- .../ParentChildCondition.php | 64 +-- .../ParentChildConditionInterface.php | 17 +- .../ModelRelationship/RootCondition.php | 15 +- .../RootConditionInterface.php | 8 +- .../Builder/Event/AddConditionEvent.php | 26 +- .../Palette/Builder/Event/BuilderEvent.php | 5 +- .../Builder/Event/CreateConditionEvent.php | 19 +- .../CreateDefaultPaletteConditionEvent.php | 21 +- .../CreatePaletteConditionChainEvent.php | 26 +- .../CreatePropertyConditionChainEvent.php | 27 +- ...tDefaultPaletteConditionClassNameEvent.php | 18 +- .../Builder/Event/SetLegendClassNameEvent.php | 13 +- .../Event/SetPaletteClassNameEvent.php | 13 +- .../SetPaletteCollectionClassNameEvent.php | 13 +- ...SetPaletteConditionChainClassNameEvent.php | 21 +- ...tePropertyValueConditionClassNameEvent.php | 18 +- .../Event/SetPropertyClassNameEvent.php | 18 +- ...etPropertyConditionChainClassNameEvent.php | 22 +- ...etPropertyValueConditionClassNameEvent.php | 18 +- .../Palette/Builder/PaletteBuilder.php | 293 +++++----- .../Palette/AbstractBoolPaletteCondition.php | 13 +- .../Palette/PaletteConditionChain.php | 10 +- .../Palette/PaletteConditionInterface.php | 11 +- .../Palette/PropertyValueCondition.php | 13 +- .../Condition/Property/BooleanCondition.php | 9 +- .../Property/DumpingPropertyCondition.php | 9 +- .../Property/PropertyConditionInterface.php | 21 +- .../Property/PropertyEditableCondition.php | 13 +- .../Property/PropertyFalseCondition.php | 88 +-- .../Property/PropertyTrueCondition.php | 86 +-- .../Property/PropertyValueCondition.php | 16 +- .../Property/PropertyVisibleCondition.php | 8 +- src/DataDefinition/Palette/Legend.php | 25 +- src/DataDefinition/Palette/Palette.php | 15 +- .../Palette/PaletteCollection.php | 10 +- .../Palette/PaletteCollectionInterface.php | 16 +- src/DataDefinition/Palette/Property.php | 26 +- .../Palette/PropertyInterface.php | 45 +- src/DataDefinitionContainer.php | 11 +- src/DataDefinitionContainerInterface.php | 9 +- src/DefaultEnvironment.php | 52 +- .../CcaDcGeneralExtension.php | 7 +- .../Compiler/AddSessionBagsPass.php | 7 +- src/EnvironmentInterface.php | 39 +- ...bstractEventDrivenEnvironmentPopulator.php | 8 +- .../ParentEnforcingListener.php | 18 +- .../TreeEnforcingListener.php | 33 +- src/Factory/DcGeneralFactory.php | 104 ++-- src/Factory/DcGeneralFactoryInterface.php | 29 +- src/Panel/AbstractElement.php | 42 +- src/Panel/DefaultFilterElement.php | 54 +- src/Panel/DefaultLimitElement.php | 62 ++- src/Panel/DefaultPanel.php | 12 +- src/Panel/DefaultPanelContainer.php | 33 +- src/Panel/DefaultSearchElement.php | 69 +-- src/Panel/DefaultSortElement.php | 70 +-- src/Panel/DefaultSubmitElement.php | 8 +- src/Panel/PanelContainerInterface.php | 23 +- src/Panel/PanelElementInterface.php | 21 +- src/Panel/PanelInterface.php | 29 +- src/Panel/SortElementInterface.php | 12 +- .../config/contao/picker_provider.yml | 6 +- .../contao/templates/dcbe_general_edit.html5 | 2 +- .../AbstractEnvironmentAwareHandler.php | 4 +- ...AbstractPropertyOverrideEditAllHandler.php | 64 ++- .../AbstractPropertyVisibilityHandler.php | 6 +- src/View/ViewTemplateInterface.php | 13 +- tests/Data/DefaultDataProviderTest.php | 37 +- 221 files changed, 5979 insertions(+), 3288 deletions(-) diff --git a/.composer-require-checker.json b/.composer-require-checker.json index ae3559522..0fb1f54bd 100644 --- a/.composer-require-checker.json +++ b/.composer-require-checker.json @@ -1,11 +1,12 @@ { "symbol-whitelist": [ "array", "bool", "false", "int", "mixed", "null", "self", "static", "parent", "string", "true", "void", - "ContaoTwigInitializeEvent", + "ContaoCommunityAlliance\\Contao\\EventDispatcher\\CcaEventDispatcherBundle", "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\EditOnlyModeException", "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\NotCreatableException", "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\NotDeletableException", - "ContaoCommunityAlliance\\Contao\\EventDispatcher\\CcaEventDispatcherBundle", + "ContaoCommunityAlliance\\DcGeneral\\DataDefinition\\Palette\\Condition\\Property\\PropertyBooleanConditionTrait", + "ContaoTwigInitializeEvent", "Contao\\Calendar", "Contao\\CalendarEventsModel", "Contao\\CalendarModel", @@ -18,6 +19,7 @@ "Contao\\NewsArchiveModel", "Contao\\NewsModel", "FOS\\HttpCache\\CacheInvalidator", - "MenAtWork\\MultiColumnWizardBundle\\Event\\GetOptionsEvent" + "MenAtWork\\MultiColumnWizardBundle\\Event\\GetOptionsEvent", + "Symfony\\Cmf\\Component\\Routing\\ChainRouterInterface" ] } diff --git a/.github/workflows/diagnostics.yml b/.github/workflows/diagnostics.yml index f6c755d93..bddb943bd 100644 --- a/.github/workflows/diagnostics.yml +++ b/.github/workflows/diagnostics.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - php: [ '7.4', '8.0', '8.1', '8.2' ] + php: [ '8.1', '8.2' ] contao: [ '~4.13.0' ] phpcq_install: [ 'update' ] output: [ '-o github-action -o default' ] diff --git a/.phpcq.yaml.dist b/.phpcq.yaml.dist index ed969b08b..8a8013bcd 100644 --- a/.phpcq.yaml.dist +++ b/.phpcq.yaml.dist @@ -56,6 +56,7 @@ phpcq: # phpmd - 0F9684B8B16B7AB0 - A4E55EA12C7C085C + - 9093F8B32E4815AA # composer-require-checker - 033E5F8D801A2F8D diff --git a/composer.json b/composer.json index 01b6afafd..d9de59be9 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "source": "https://github.com/contao-community-alliance/dc-general" }, "require": { - "php": "^7.4 || ^8.0", + "php": "^8.1", "ext-json": "*", "ext-pdo": "*", "contao-community-alliance/events-contao-bindings": "^4.13", diff --git a/src/Clipboard/ClipboardInterface.php b/src/Clipboard/ClipboardInterface.php index 1e76d964b..45470c93b 100644 --- a/src/Clipboard/ClipboardInterface.php +++ b/src/Clipboard/ClipboardInterface.php @@ -114,7 +114,7 @@ public function hasId(ModelIdInterface $modelId); * * @param FilterInterface $filter An item filter. * - * @return ItemInterface[] + * @return list */ public function fetch(FilterInterface $filter); diff --git a/src/Clipboard/Filter.php b/src/Clipboard/Filter.php index 9a8a9accd..a3115ad09 100644 --- a/src/Clipboard/Filter.php +++ b/src/Clipboard/Filter.php @@ -22,7 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Clipboard; -use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use function count; @@ -252,11 +252,11 @@ private function modelIsNotFromProvider($conjunction, $modelProviderName) /** * And model is. * - * @param ModelId $modelId The model id. + * @param ModelIdInterface $modelId The model id. * * @return static */ - public function andModelIs(ModelId $modelId) + public function andModelIs(ModelIdInterface $modelId) { $this->modelIs('and', $modelId); @@ -266,11 +266,11 @@ public function andModelIs(ModelId $modelId) /** * Or model is. * - * @param ModelId $modelId The model id. + * @param ModelIdInterface $modelId The model id. * * @return static */ - public function orModelIs(ModelId $modelId) + public function orModelIs(ModelIdInterface $modelId) { $this->modelIs('or', $modelId); @@ -280,12 +280,12 @@ public function orModelIs(ModelId $modelId) /** * Add model is. * - * @param string $conjunction AND or OR. - * @param ModelId $modelId The model id. + * @param string $conjunction AND or OR. + * @param ModelIdInterface $modelId The model id. * * @return static */ - private function modelIs($conjunction, ModelId $modelId) + private function modelIs($conjunction, ModelIdInterface $modelId) { if (!empty($this->expression)) { $this->expression[] = $conjunction; @@ -302,11 +302,11 @@ private function modelIs($conjunction, ModelId $modelId) /** * And model is not. * - * @param ModelId $modelId The model id. + * @param ModelIdInterface $modelId The model id. * * @return static */ - public function andModelIsNot(ModelId $modelId) + public function andModelIsNot(ModelIdInterface $modelId) { $this->modelIsNot('and', $modelId); @@ -316,11 +316,11 @@ public function andModelIsNot(ModelId $modelId) /** * Or model is not. * - * @param ModelId $modelId The model id. + * @param ModelIdInterface $modelId The model id. * * @return static */ - public function orModelIsNot(ModelId $modelId) + public function orModelIsNot(ModelIdInterface $modelId) { $this->modelIsNot('or', $modelId); @@ -330,12 +330,12 @@ public function orModelIsNot(ModelId $modelId) /** * Add model is not. * - * @param string $conjunction AND or OR. - * @param ModelId $modelId The model id. + * @param string $conjunction AND or OR. + * @param ModelIdInterface $modelId The model id. * * @return static */ - private function modelIsNot($conjunction, ModelId $modelId) + private function modelIsNot($conjunction, ModelIdInterface $modelId) { if (!empty($this->expression)) { $this->expression[] = $conjunction; @@ -495,11 +495,11 @@ private function hasNoParent($conjunction) /** * And parent is. * - * @param ModelId $parentModelId The parent id. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - public function andParentIs(ModelId $parentModelId) + public function andParentIs(ModelIdInterface $parentModelId) { $this->parentIs('and', $parentModelId); @@ -509,11 +509,11 @@ public function andParentIs(ModelId $parentModelId) /** * Or parent is. * - * @param ModelId $parentModelId The parent id. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - public function orParentIs(ModelId $parentModelId) + public function orParentIs(ModelIdInterface $parentModelId) { $this->parentIs('or', $parentModelId); @@ -523,12 +523,12 @@ public function orParentIs(ModelId $parentModelId) /** * Add parent is. * - * @param string $conjunction AND or OR. - * @param ModelId $parentModelId The parent id. + * @param string $conjunction AND or OR. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - private function parentIs($conjunction, ModelId $parentModelId) + private function parentIs($conjunction, ModelIdInterface $parentModelId) { if (!empty($this->expression)) { $this->expression[] = $conjunction; @@ -545,7 +545,7 @@ private function parentIs($conjunction, ModelId $parentModelId) /** * And parent is in. * - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ @@ -559,7 +559,7 @@ public function andParentIsIn(array $parentModelIds) /** * Or parent is in. * - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ @@ -574,7 +574,7 @@ public function orParentIsIn(array $parentModelIds) * Add parent is in. * * @param string $conjunction AND or OR. - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ @@ -599,11 +599,11 @@ private function parentIsIn($conjunction, array $parentModelIds) /** * And parent is not. * - * @param ModelId $parentModelId The parent id. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - public function andParentIsNot(ModelId $parentModelId) + public function andParentIsNot(ModelIdInterface $parentModelId) { $this->parentIsNot('and', $parentModelId); @@ -613,11 +613,11 @@ public function andParentIsNot(ModelId $parentModelId) /** * Or parent is not. * - * @param ModelId $parentModelId The parent id. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - public function orParentIsNot(ModelId $parentModelId) + public function orParentIsNot(ModelIdInterface $parentModelId) { $this->parentIsNot('and', $parentModelId); @@ -627,12 +627,12 @@ public function orParentIsNot(ModelId $parentModelId) /** * Add parent is not. * - * @param string $conjunction AND or OR. - * @param ModelId $parentModelId The parent id. + * @param string $conjunction AND or OR. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - private function parentIsNot($conjunction, ModelId $parentModelId) + private function parentIsNot($conjunction, ModelIdInterface $parentModelId) { if (!empty($this->expression)) { $this->expression[] = $conjunction; @@ -649,7 +649,7 @@ private function parentIsNot($conjunction, ModelId $parentModelId) /** * And parent is not in. * - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ @@ -663,7 +663,7 @@ public function andParentIsNotIn(array $parentModelIds) /** * Or parent is not in. * - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ @@ -678,7 +678,7 @@ public function orParentIsNotIn(array $parentModelIds) * Add parent is not in. * * @param string $conjunction AND or OR. - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ diff --git a/src/Contao/Callback/ModelLabelCallbackListener.php b/src/Contao/Callback/ModelLabelCallbackListener.php index c4e478ba9..59db1cc6e 100644 --- a/src/Contao/Callback/ModelLabelCallbackListener.php +++ b/src/Contao/Callback/ModelLabelCallbackListener.php @@ -62,6 +62,7 @@ public function update($event, $value) if (!\is_array($value)) { return; } + /** @var list $value */ $this->updateTableMode($event, $value); } @@ -102,12 +103,12 @@ private function updateNonTableMode(ModelToLabelEvent $event, ?string $value): v /** * Set the value in the event. * - * @param ModelToLabelEvent $event The event being emitted. - * @param array $arguments The label arguments. + * @param ModelToLabelEvent $event The event being emitted. + * @param string|list $arguments The label arguments. * * @return void */ - private function updateTableMode(ModelToLabelEvent $event, array $arguments): void + private function updateTableMode(ModelToLabelEvent $event, array|string $arguments): void { if (empty($arguments)) { return; diff --git a/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php b/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php index 31d12da94..56d46ae59 100644 --- a/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php +++ b/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,13 +14,15 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\Callback; +use Contao\Widget; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; @@ -50,6 +52,9 @@ public function getArgs($event) */ public function update($event, $value) { - $event->getWidget()->wizard .= $value; + $widget = $event->getWidget(); + if ($widget instanceof Widget) { + $widget->wizard .= $value; + } } } diff --git a/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php b/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php index 5449ef866..044d1e110 100644 --- a/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php +++ b/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,13 +13,15 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\Callback; +use Contao\Widget; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; @@ -49,6 +51,9 @@ public function getArgs($event) */ public function update($event, $value) { - $event->getWidget()->xlabel .= $value; + $widget = $event->getWidget(); + if ($widget instanceof Widget) { + $widget->wizard .= $value; + } } } diff --git a/src/Contao/Compatibility/DcCompat.php b/src/Contao/Compatibility/DcCompat.php index a1d08c0f4..6d1fefa8f 100644 --- a/src/Contao/Compatibility/DcCompat.php +++ b/src/Contao/Compatibility/DcCompat.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,7 +31,7 @@ use ContaoCommunityAlliance\DcGeneral\Factory\Event\PopulateEnvironmentEvent; /** - * Small compatibility layer for callbacks, that expect a "full featured" DC instance. + * Small compatibility layer for callbacks, that expect a "full-featured" DC instance. */ class DcCompat extends General { diff --git a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php index fc683e597..9c35455e6 100644 --- a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php +++ b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php @@ -876,8 +876,10 @@ protected function parseListSorting(ListingConfigInterface $listing, array $list $parsedProperties = []; $sortingDca = ($listDca['sorting'] ?? []); - if (isset($sortingDca['headerFields'])) { - $listing->setHeaderPropertyNames((array) $sortingDca['headerFields']); + if ($headerFields = ($sortingDca['headerFields'] ?? [])) { + assert(\is_array($headerFields)); + /** @var list $headerFields */ + $listing->setHeaderPropertyNames($headerFields); } if (isset($sortingDca['icon'])) { diff --git a/src/Contao/Dca/Populator/BackendViewPopulator.php b/src/Contao/Dca/Populator/BackendViewPopulator.php index 01a39f4e6..977a29f85 100644 --- a/src/Contao/Dca/Populator/BackendViewPopulator.php +++ b/src/Contao/Dca/Populator/BackendViewPopulator.php @@ -75,6 +75,8 @@ public function __construct( ) { if (null === $tokenManager) { $tokenManager = System::getContainer()->get('security.csrf.token_manager'); + assert($tokenManager instanceof CsrfTokenManagerInterface); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token manager as 2th argument to "' . __METHOD__ . '" is deprecated ' . @@ -85,6 +87,8 @@ public function __construct( } if (null === $tokenName) { $tokenName = System::getContainer()->getParameter('contao.csrf_token_name'); + assert(\is_string($tokenName)); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token name as 3th argument to "' . __METHOD__ . '" is deprecated ' . diff --git a/src/Contao/Dca/Populator/HardCodedPopulator.php b/src/Contao/Dca/Populator/HardCodedPopulator.php index 8ce72df1c..f7d4ff57f 100644 --- a/src/Contao/Dca/Populator/HardCodedPopulator.php +++ b/src/Contao/Dca/Populator/HardCodedPopulator.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -71,11 +72,15 @@ public function populateController(EnvironmentInterface $environment) public function populate(EnvironmentInterface $environment) { if (!$environment->getSessionStorage()) { - /** @var SessionStorageFactory $sessionFactory */ $sessionFactory = System::getContainer()->get('cca.dc-general.session_factory'); - $definition = $environment->getDataDefinition(); + assert($sessionFactory instanceof SessionStorageFactory); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $sessionStorage = $sessionFactory->createService(); assert($definition instanceof ContainerInterface); + $sessionStorage->setScope('DC_GENERAL_' . \strtoupper($definition->getName())); $environment->setSessionStorage($sessionStorage); // @codingStandardsIgnoreStart diff --git a/src/Contao/Factory/SessionStorageFactory.php b/src/Contao/Factory/SessionStorageFactory.php index 0247535a0..bec33a78e 100644 --- a/src/Contao/Factory/SessionStorageFactory.php +++ b/src/Contao/Factory/SessionStorageFactory.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -55,7 +56,7 @@ public function createService() $session = $this->container->get('session'); assert($session instanceof SessionInterface); $keys = $this->container->getParameter('cca.dc-general.session.database_keys'); - assert(is_array($keys)); + assert(\is_array($keys)); /** @var list $keys */ return new SessionStorage($session, $keys); diff --git a/src/Contao/Picker/PagePickerProvider.php b/src/Contao/Picker/PagePickerProvider.php index 52bbfe420..35a7563ef 100644 --- a/src/Contao/Picker/PagePickerProvider.php +++ b/src/Contao/Picker/PagePickerProvider.php @@ -22,12 +22,46 @@ use Contao\CoreBundle\Picker\AbstractPickerProvider; use Contao\CoreBundle\Picker\DcaPickerProviderInterface; use Contao\CoreBundle\Picker\PickerConfig; +use Contao\System; +use Knp\Menu\FactoryInterface; +use Symfony\Component\Routing\RouterInterface; +use Symfony\Component\Security\Core\Security; +use Symfony\Contracts\Translation\TranslatorInterface; /** * Provides the page picker. */ class PagePickerProvider extends AbstractPickerProvider implements DcaPickerProviderInterface { + private Security $security; + + /** + * @internal + */ + public function __construct( + FactoryInterface $menuFactory, + RouterInterface $router, + ?TranslatorInterface $translator, + ?Security $security + ) { + parent::__construct($menuFactory, $router, $translator); + + if (null === $security) { + $security = System::getContainer()->get('security.helper'); + assert($security instanceof Security); + + // @codingStandardsIgnoreStart + @trigger_error( + 'Not passing the security as argument to "' . __METHOD__ . '" is deprecated ' . + 'and will cause an error in DCG 3.0', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + } + + $this->security = $security; + } + /** * {@inheritdoc} */ @@ -41,7 +75,8 @@ public function getName() */ public function supportsContext($context) { - return \in_array($context, ['cca_ page', 'cca_link'], true) && $this->getUser()->hasAccess('page', 'modules'); + return \in_array($context, ['cca_page', 'cca_link'], true) + && $this->security->isGranted('contao_user.modules', 'page'); } /** diff --git a/src/Contao/SessionStorage.php b/src/Contao/SessionStorage.php index d5354c705..b6d7956a2 100644 --- a/src/Contao/SessionStorage.php +++ b/src/Contao/SessionStorage.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2022 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao; use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface; /** @@ -32,9 +34,9 @@ class SessionStorage implements SessionStorageInterface /** * The session scope. * - * @var string + * @var null|string */ - private $scope; + private ?string $scope = null; /** * The symfony session. @@ -99,7 +101,6 @@ public function setScope($scope) // @codingStandardsIgnoreStart @\trigger_error('The scope can not be change! Use a new session storage.', E_USER_ERROR); // @codingStandardsIgnoreEnd - return; } $this->scope = $scope; @@ -175,6 +176,7 @@ public function clear() $this->load(); $this->attributes = []; $this->persist(); + return $this; } /** @@ -190,11 +192,13 @@ private function load() $sessionBag = $this->session->getBag($this->getSessionBagKey()); $databaseSessionBag = $this->session->getBag($this->getDatabaseSessionBagKey()); + assert($sessionBag instanceof AttributeBagInterface); + assert($databaseSessionBag instanceof AttributeBagInterface); - $this->attributes = \array_merge( - (array) $sessionBag->get($this->getScope()), - (array) $databaseSessionBag->get($this->getScope()) - ); + if (null === $scope = $this->getScope()) { + return; + } + $this->attributes = \array_merge((array) $sessionBag->get($scope), (array) $databaseSessionBag->get($scope)); } /** @@ -204,11 +208,17 @@ private function load() */ private function persist() { + if (null === $scope = $this->getScope()) { + return; + } + $sessionBag = $this->session->getBag($this->getSessionBagKey()); - $sessionBag->set($this->getScope(), $this->filterAttributes()); + assert($sessionBag instanceof AttributeBagInterface); + $sessionBag->set($scope, $this->filterAttributes()); $databaseSessionBag = $this->session->getBag($this->getDatabaseSessionBagKey()); - $databaseSessionBag->set($this->getScope(), $this->filterAttributes(true)); + assert($databaseSessionBag instanceof AttributeBagInterface); + $databaseSessionBag->set($scope, $this->filterAttributes(true)); } /** @@ -221,10 +231,10 @@ private function persist() */ private function filterAttributes($determineDatabase = false) { - $databaseAttributes = \array_merge( - $this->databaseKeys['common'] ?? [], - $this->databaseKeys[$this->getScope()] ?? [] - ); + $databaseAttributes = $this->databaseKeys['common'] ?? []; + if ($scope = $this->getScope()) { + $databaseAttributes = \array_merge($databaseAttributes, $this->databaseKeys[$scope] ?? []); + } if ($determineDatabase) { return \array_intersect_key($this->attributes, \array_flip($databaseAttributes)); diff --git a/src/Contao/Subscriber/DynamicParentTableSubscriber.php b/src/Contao/Subscriber/DynamicParentTableSubscriber.php index 017a3ab46..0667b7d01 100644 --- a/src/Contao/Subscriber/DynamicParentTableSubscriber.php +++ b/src/Contao/Subscriber/DynamicParentTableSubscriber.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -44,7 +45,7 @@ class DynamicParentTableSubscriber implements EventSubscriberInterface * * array('eventName' => array('methodName', $priority)) * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) * - * @return array The event names to listen to. + * @return array The event names to listen to. */ public static function getSubscribedEvents() { @@ -62,11 +63,12 @@ public static function getSubscribedEvents() */ public function handlePrePersistModelEvent(PrePersistModelEvent $event) { - $enviroment = $event->getEnvironment(); - $dataDefinition = $enviroment->getDataDefinition(); + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); if ( - null === ($parentDataDefinition = $enviroment->getParentDataDefinition()) + null === $dataDefinition + || null === ($parentDataDefinition = $environment->getParentDataDefinition()) || (false === $dataDefinition->getPropertiesDefinition()->hasProperty('ptable')) || (false === $dataDefinition->getBasicDefinition()->isDynamicParentTable()) ) { diff --git a/src/Contao/Subscriber/FallbackResetSubscriber.php b/src/Contao/Subscriber/FallbackResetSubscriber.php index da4222c5b..599c7cf5b 100644 --- a/src/Contao/Subscriber/FallbackResetSubscriber.php +++ b/src/Contao/Subscriber/FallbackResetSubscriber.php @@ -24,7 +24,10 @@ use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelManipulator; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\Event\AbstractModelAwareEvent; use ContaoCommunityAlliance\DcGeneral\Event\PostDuplicateModelEvent; use ContaoCommunityAlliance\DcGeneral\Event\PostPersistModelEvent; @@ -51,7 +54,7 @@ class FallbackResetSubscriber implements EventSubscriberInterface * * array('eventName' => array('methodName', $priority)) * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) * - * @return array The event names to listen to. + * @return array The event names to listen to. */ public static function getSubscribedEvents() { @@ -91,21 +94,32 @@ public function handlePostDuplicateModelEvent(PostDuplicateModelEvent $event) * @param AbstractModelAwareEvent $event The event. * * @return void + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ private function handleFallback(AbstractModelAwareEvent $event) { - $model = $event->getModel(); + $model = $event->getModel(); + assert($model instanceof ModelInterface); + $dataProvider = $event->getEnvironment()->getDataProvider($model->getProviderName()); - $properties = $event->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + assert($dataProvider instanceof DataProviderInterface); + + $definition = $event->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); foreach (\array_keys($model->getPropertiesAsArray()) as $propertyName) { if (!$properties->hasProperty($propertyName)) { continue; } + $property = $properties->getProperty($propertyName); - $extra = (array) $property->getExtra(); + $extra = $property->getExtra(); + if (\array_key_exists('fallback', $extra) && (true === $extra['fallback'])) { - // BC Layer - use old reset fallback methodology until it get's removed. + // BC Layer - use old reset fallback methodology until it gets removed. if (null === ($config = $this->determineFilterConfig($event))) { // @codingStandardsIgnoreStart @\trigger_error( @@ -114,8 +128,10 @@ private function handleFallback(AbstractModelAwareEvent $event) E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - + /** @psalm-suppress DeprecatedMethod */ $dataProvider->resetFallback($propertyName); + $dataProvider->save($model); + continue; } // If value is empty, no need to reset the fallback. @@ -126,9 +142,14 @@ private function handleFallback(AbstractModelAwareEvent $event) $models = $dataProvider->fetchAll($config); foreach ($models as $resetModel) { + if (!($resetModel instanceof ModelInterface)) { + continue; + } + if ($model->getId() === $resetModel->getId()) { continue; } + $resetModel->setProperty($propertyName, ModelManipulator::sanitizeValue($property, null)); $dataProvider->save($resetModel); } @@ -142,13 +163,23 @@ private function handleFallback(AbstractModelAwareEvent $event) * @param AbstractModelAwareEvent $event The event. * * @return ConfigInterface|null + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(NPathComplexity) */ private function determineFilterConfig(AbstractModelAwareEvent $event) { $environment = $event->getEnvironment(); $model = $event->getModel(); - $dataProvider = $environment->getDataProvider($model->getProviderName()); - $definition = $environment->getDataDefinition(); + + if (null === ($dataProvider = $environment->getDataProvider($model->getProviderName()))) { + return null; + } + + if (null === ($definition = $environment->getDataDefinition())) { + return null; + } + $relationship = $definition->getModelRelationshipDefinition(); $root = $relationship->getRootCondition(); @@ -156,8 +187,12 @@ private function determineFilterConfig(AbstractModelAwareEvent $event) return $dataProvider->getEmptyConfig()->setFilter($root->getFilterArray()); } + if (null === ($parentDefinition = $definition->getBasicDefinition()->getParentDataProvider())) { + return null; + } + $parentFilter = $relationship->getChildCondition( - $definition->getBasicDefinition()->getParentDataProvider(), + $parentDefinition, $model->getProviderName() ); diff --git a/src/Contao/Subscriber/FormatModelLabelSubscriber.php b/src/Contao/Subscriber/FormatModelLabelSubscriber.php index 449bf3a18..c08a06ee4 100644 --- a/src/Contao/Subscriber/FormatModelLabelSubscriber.php +++ b/src/Contao/Subscriber/FormatModelLabelSubscriber.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Tristan Lins * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,6 +27,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PropertiesDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; @@ -57,6 +58,8 @@ public function handleFormatModelLabel(FormatModelLabelEvent $event) $model = $event->getModel(); $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $viewSection */ $viewSection = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); $listing = $viewSection->getListingConfig(); @@ -72,7 +75,11 @@ public function handleFormatModelLabel(FormatModelLabelEvent $event) ->setLabel($formatter->getFormat()) ->setFormatter($formatter); - $environment->getEventDispatcher()->dispatch($modelToLabelEvent, ModelToLabelEvent::NAME); + if (null === ($dispatcher = $environment->getEventDispatcher())) { + return; + } + + $dispatcher->dispatch($modelToLabelEvent, ModelToLabelEvent::NAME); // Add columns. if ($listing->getShowColumns()) { @@ -126,7 +133,7 @@ private function getFirstSorting(GroupAndSortingDefinitionInterface $sortingDefi * @param EnvironmentInterface $environment The environment. * @param ModelInterface $model The model. * - * @return array + * @return array */ private function prepareLabelArguments( $propertyNames, @@ -155,13 +162,13 @@ private function prepareLabelArguments( /** * Render for column layout. * - * @param string[] $propertyNames The properties. - * @param string[] $args The rendered arguments. - * @param string $firstSorting The sorting column. + * @param string[] $propertyNames The properties. + * @param string|array $args The rendered arguments. + * @param string $firstSorting The sorting column. * * @return array */ - private function renderWithColumns($propertyNames, $args, $firstSorting) + private function renderWithColumns(array $propertyNames, array|string $args, string $firstSorting) { $label = []; if (!\is_array($args)) { @@ -194,13 +201,13 @@ private function renderWithColumns($propertyNames, $args, $firstSorting) /** * Render as single value. * - * @param string $label The label string. - * @param string[] $args The rendered arguments. - * @param null $maxLength The maximum length for the label or null to allow unlimited. + * @param string $label The label string. + * @param string|array $args The rendered arguments. + * @param null|int $maxLength The maximum length for the label or null to allow unlimited. * * @return string */ - private function renderSingleValue($label, $args, $maxLength = null) + private function renderSingleValue(string $label, array|string $args, ?int $maxLength = null) { // BC: sometimes the label was returned as string in the arguments instead of an array. $string = !\is_array($args) ? $args : \vsprintf($label, $args); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php index 5821695dd..e7aacce10 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php @@ -32,10 +32,14 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistry; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; +use ContaoCommunityAlliance\DcGeneral\Clipboard\FilterInterface; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\BackendViewInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ButtonRenderer; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; @@ -44,12 +48,16 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\GlobalButtonRenderer; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\PanelRenderer; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionCollectionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; @@ -58,15 +66,17 @@ use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Event\FormatModelLabelEvent; use ContaoCommunityAlliance\DcGeneral\Event\ViewEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; use ContaoCommunityAlliance\Translator\TranslatorInterface as CcaTranslator; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Contracts\Translation\TranslatorInterface; use function array_key_exists; use function implode; -use function in_array; -use function sprintf; use function str_replace; use function strpos; use function trigger_error; @@ -92,9 +102,9 @@ abstract class AbstractListShowAllHandler /** * The cca translator. * - * @var CcaTranslator|TranslatorInterface + * @var CcaTranslator */ - private $ccaTranslator; + private CcaTranslator $ccaTranslator; /** * The token manager. @@ -113,11 +123,11 @@ abstract class AbstractListShowAllHandler /** * AbstractHandler constructor. * - * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator. - * @param TranslatorInterface $translator The translator. - * @param CcaTranslator|TranslatorInterface $ccaTranslator The cca translator. - * @param CsrfTokenManagerInterface|null $tokenManager The token manager. - * @param string|null $tokenName The token name. + * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator. + * @param TranslatorInterface $translator The translator. + * @param CcaTranslator $ccaTranslator The cca translator. + * @param CsrfTokenManagerInterface|null $tokenManager The token manager. + * @param string|null $tokenName The token name. */ public function __construct( RequestScopeDeterminator $scopeDeterminator, @@ -133,6 +143,8 @@ public function __construct( if (null === $tokenManager) { $tokenManager = System::getContainer()->get('security.csrf.token_manager'); + assert($tokenManager instanceof CsrfTokenManagerInterface); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token manager as 4th argument to "' . __METHOD__ . '" is deprecated ' . @@ -143,6 +155,8 @@ public function __construct( } if (null === $tokenName) { $tokenName = System::getContainer()->getParameter('contao.csrf_token_name'); + assert(\is_string($tokenName)); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token name as 5th argument to "' . __METHOD__ . '" is deprecated ' . @@ -170,18 +184,23 @@ public function handleEvent(ActionEvent $event) } $environment = $event->getEnvironment(); - $basic = $environment->getDataDefinition()->getBasicDefinition(); - $action = $event->getAction(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basic = $definition->getBasicDefinition(); + $action = $event->getAction(); if ( - null !== $event->getResponse() + (null === $mode = $basic->getMode()) + || null !== $event->getResponse() || ('showAll' !== $action->getName()) - || !$this->wantToHandle($basic->getMode(), $action) + || !$this->wantToHandle($mode, $action) ) { return; } - if (false !== ($response = $this->process($action, $environment))) { + if (null !== ($response = $this->process($action, $environment))) { $event->setResponse($response); } } @@ -192,12 +211,15 @@ public function handleEvent(ActionEvent $event) * @param Action $action The action being handled. * @param EnvironmentInterface $environment Current dc-general environment. * - * @return string + * @return string|null */ protected function process(Action $action, EnvironmentInterface $environment) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + // Edit only mode, forward to edit action. - if ($environment->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->callAction($environment, 'edit', $action->getArguments()); } @@ -207,17 +229,22 @@ protected function process(Action $action, EnvironmentInterface $environment) // Process now. $collection = $this->loadCollection($environment); + assert($collection instanceof CollectionInterface); $this->handleEditAllButton($collection, $environment); - $this->renderCollection($environment, $collection, $grouping); + $this->renderCollection($environment, $collection, $grouping ?: []); - $template = $this->determineTemplate($grouping) + $template = $this->determineTemplate($grouping ?: []); + $template ->set('collection', $collection) ->set('mode', ($grouping ? $grouping['mode'] : null)) ->set('theme', Backend::getTheme()); $this->renderTemplate($template, $environment); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $clipboard = new ViewEvent($environment, $action, DcGeneralViews::CLIPBOARD, []); - $environment->getEventDispatcher()->dispatch($clipboard, DcGeneralEvents::VIEW); + $dispatcher->dispatch($clipboard, DcGeneralEvents::VIEW); return implode( "\n", @@ -232,7 +259,7 @@ protected function process(Action $action, EnvironmentInterface $environment) } /** - * Execute the multi language support. + * Execute the multi-language support. * * @param EnvironmentInterface $environment The environment. * @@ -247,10 +274,12 @@ private function languageSwitcher(EnvironmentInterface $environment) return ''; } - /** @var MultiLanguageDataProviderInterface $dataProvider */ + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + /** @var MultiLanguageDataProviderInterface $dataProvider */ return $template - ->set('languages', $environment->getController()->getSupportedLanguages(null)) + ->set('languages', $controller->getSupportedLanguages(null)) ->set('language', $dataProvider->getCurrentLanguage()) ->set('submit', $this->translator->trans('MSC.showSelected', [], 'contao_default')) ->set('REQUEST_TOKEN', $this->tokenManager->getToken($this->tokenName)) @@ -262,20 +291,22 @@ private function languageSwitcher(EnvironmentInterface $environment) * * @param ContainerInterface $definition Data container definition. * - * @return DefinitionInterface|Contao2BackendViewDefinitionInterface + * @return Contao2BackendViewDefinitionInterface */ protected function getViewSection(ContainerInterface $definition) { - return $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $viewDefinition = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($viewDefinition instanceof Contao2BackendViewDefinitionInterface); + return $viewDefinition; } /** * Check if the action should be handled. * - * @param string $mode The list mode. + * @param int $mode The list mode. * @param Action $action The action. * - * @return mixed + * @return bool */ abstract protected function wantToHandle($mode, Action $action); @@ -286,7 +317,7 @@ abstract protected function wantToHandle($mode, Action $action); * @param string|null $domain The domain name to use. * @param array $parameters Parameters. * - * @return array|string + * @return string */ protected function translate($key, $domain, array $parameters = []) { @@ -303,7 +334,11 @@ protected function translate($key, $domain, array $parameters = []) // @codingStandardsIgnoreEnd $translated = - $this->translator->trans(sprintf('%s.%s', $domain, $key), $parameters, sprintf('contao_%s', $domain)); + $this->translator->trans( + \sprintf('%s.%s', $domain ?? '', $key), + $parameters, + \sprintf('contao_%s', $domain ?? '') + ); } return $translated; @@ -319,8 +354,11 @@ protected function translate($key, $domain, array $parameters = []) */ protected function renderModel(ModelInterface $model, EnvironmentInterface $environment) { + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $event = new FormatModelLabelEvent($environment, $model); - $environment->getEventDispatcher()->dispatch($event, DcGeneralEvents::FORMAT_MODEL_LABEL); + $dispatcher->dispatch($event, DcGeneralEvents::FORMAT_MODEL_LABEL); $model->setMeta($model::LABEL_VALUE, $event->getLabel()); } @@ -357,12 +395,17 @@ abstract protected function determineTemplate($groupingInformation); protected function renderTemplate(ContaoBackendViewTemplate $template, EnvironmentInterface $environment) { $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $provider = $environment->getInputProvider(); + assert($provider instanceof InputProviderInterface); + $showColumn = $this->getViewSection($definition)->getListingConfig()->getShowColumns(); $template ->set('subHeadline', $this->translate('MSC.select_models', 'contao_default')) - ->set('tableName', ($definition->getName() ?? 'none')) - ->set('select', 'select' === $environment->getInputProvider()->getParameter('act')) + ->set('tableName', ($definition->getName() ?: 'none')) + ->set('select', 'select' === $provider->getParameter('act')) ->set('action', StringUtil::ampersand(Environment::get('request'))) ->set('selectButtons', $this->getSelectButtons($environment)) ->set('sortable', $this->isSortable($environment)) @@ -390,17 +433,30 @@ protected function renderTemplate(ContaoBackendViewTemplate $template, Environme * * @param EnvironmentInterface $environment The environment. * - * @return CollectionInterface + * @return CollectionInterface|list */ protected function loadCollection(EnvironmentInterface $environment) { - $dataConfig = $environment->getBaseConfigRegistry()->getBaseConfig(); - $listingConfig = $this->getViewSection($environment->getDataDefinition())->getListingConfig(); - $panel = $environment->getView()->getPanel(); + $config = $environment->getBaseConfigRegistry(); + assert($config instanceof BaseConfigRegistry); + + $view = $environment->getView(); + assert($view instanceof BackendViewInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $dataConfig = $config->getBaseConfig(); + $listingConfig = $this->getViewSection($definition)->getListingConfig(); + $panel = $view->getPanel(); + assert($panel instanceof PanelContainerInterface); ViewHelpers::initializeSorting($panel, $dataConfig, $listingConfig); - return $environment->getDataProvider()->fetchAll($dataConfig); + $provider = $environment->getDataProvider(); + assert($provider instanceof DataProviderInterface); + + return $provider->fetchAll($dataConfig); } /** @@ -424,15 +480,25 @@ private function generateHeaderButtons(EnvironmentInterface $environment) * * @return void */ - private function renderCollection(EnvironmentInterface $environment, CollectionInterface $collection, $grouping) - { - $listing = $this->getViewSection($environment->getDataDefinition())->getListingConfig(); + private function renderCollection( + EnvironmentInterface $environment, + CollectionInterface $collection, + array $grouping + ) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $listing = $this->getViewSection($definition)->getListingConfig(); $remoteCur = null; $groupClass = 'tl_folder_tlist'; $eoCount = -1; + if (null === ($inputProvider = $environment->getInputProvider())) { + return; + } + // Generate buttons - only if not in select mode! - if ('select' !== $environment->getInputProvider()->getParameter('act')) { + if ('select' !== $inputProvider->getParameter('act')) { $buttonRenderer = new ButtonRenderer($environment); $buttonRenderer->renderButtonsForCollection($collection); } @@ -440,7 +506,7 @@ private function renderCollection(EnvironmentInterface $environment, CollectionI // Run each model. foreach ($collection as $model) { /** @var ModelInterface $model */ - $this->addGroupHeader($environment, (array) $grouping, $model, $groupClass, $eoCount, $remoteCur); + $this->addGroupHeader($environment, $grouping, $model, $groupClass, $eoCount, $remoteCur); if ($listing->getItemCssClass()) { $model->setMeta($model::CSS_CLASS, $listing->getItemCssClass()); @@ -451,7 +517,11 @@ private function renderCollection(EnvironmentInterface $environment, CollectionI $cssClasses[] = $model->getMeta($model::CSS_ROW_CLASS) : null; $modelId = ModelId::fromModel($model); - if ($environment->getClipboard()->hasId($modelId)) { + + if ( + null !== ($clipboard = $environment->getClipboard()) + && $clipboard->hasId($modelId) + ) { $cssClasses[] = 'tl_folder_clipped'; } @@ -516,7 +586,10 @@ private function addGroupHeader( */ private function panel(EnvironmentInterface $environment, $ignoredPanels = []) { - return (new PanelRenderer($environment->getView()))->render($ignoredPanels); + $view = $environment->getView(); + assert($view instanceof BackendViewInterface); + + return (new PanelRenderer($view))->render($ignoredPanels); } /** @@ -528,15 +601,17 @@ private function panel(EnvironmentInterface $environment, $ignoredPanels = []) */ private function getTableHead(EnvironmentInterface $environment) { - $tableHead = []; $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $tableHead = []; $properties = $definition->getPropertiesDefinition(); $formatter = $this->getViewSection($definition)->getListingConfig()->getLabelFormatter($definition->getName()); $sorting = ViewHelpers::getCurrentSorting($environment); $columns = $this->getSortingColumns($sorting); foreach ($formatter->getPropertyNames() as $field) { $tableHead[] = [ - 'class' => 'tl_folder_tlist col_' . $field . (in_array($field, $columns) ? ' ordered_by' : ''), + 'class' => 'tl_folder_tlist col_' . $field . (\in_array($field, $columns) ? ' ordered_by' : ''), 'content' => $properties->hasProperty($field) ? $properties->getProperty($field)->getLabel() : $this->translate($definition->getName() . '.' . $field . '.0', 'contao_' . $definition->getName()) @@ -560,7 +635,7 @@ private function getTableHead(EnvironmentInterface $environment) * @param int $groupLength The length of the value to use for grouping (only used when grouping. * @param EnvironmentInterface $environment The environment. * - * @return string + * @return string|null * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) @@ -572,13 +647,19 @@ private function renderGroupHeader( $groupLength, EnvironmentInterface $environment ) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + // No property? Get out! - if (!$environment->getDataDefinition()->getPropertiesDefinition()->getProperty($field)) { + if (!$definition->getPropertiesDefinition()->hasProperty($field)) { return '-'; } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $event = new GetGroupHeaderEvent($environment, $model, $field, null, $groupMode, $groupLength); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); return $event->getValue(); } @@ -594,7 +675,11 @@ protected function getSelectButtons(EnvironmentInterface $environment) { $event = new GetSelectModeButtonsEvent($environment); $event->setButtons([]); - $environment->getEventDispatcher()->dispatch($event, GetSelectModeButtonsEvent::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, GetSelectModeButtonsEvent::NAME); return $event->getButtons(); } @@ -608,32 +693,54 @@ protected function getSelectButtons(EnvironmentInterface $environment) */ private function isSortable(EnvironmentInterface $environment) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + return ((true === (bool) ViewHelpers::getManualSortingProperty($environment)) - && (true === $environment->getDataDefinition()->getBasicDefinition()->isEditable())); + && (true === $definition->getBasicDefinition()->isEditable())); } /** * Render paste top button. Returns null if no button should be rendered. * - * @param EnvironmentInterface $environment The environment. - * @param GroupAndSortingDefinitionInterface|null $sorting The sorting mode. + * @param EnvironmentInterface $environment The + * env. + * @param GroupAndSortingDefinitionInterface|GroupAndSortingDefinitionCollectionInterface|null $sorting The + * sorting + * mode. * * @return string */ protected function renderPasteTopButton(EnvironmentInterface $environment, $sorting) { - $definition = $environment->getDataDefinition(); - $dispatcher = $environment->getEventDispatcher(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $languageDomain = 'contao_' . $definition->getName(); $filter = new Filter(); - $filter->andModelIsFromProvider($definition->getBasicDefinition()->getDataProvider()); + assert($filter instanceof FilterInterface); - if (!$sorting || $environment->getClipboard()->isEmpty($filter)) { - return null; + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $filter->andModelIsFromProvider($dataProvider); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + if (!$sorting || $clipboard->isEmpty($filter)) { + return ''; } + if (!ViewHelpers::getManualSortingProperty($environment)) { - return null; + return ''; } /** @var AddToUrlEvent $urlEvent */ @@ -654,11 +761,11 @@ protected function renderPasteTopButton(EnvironmentInterface $environment, $sort ContaoEvents::IMAGE_GET_HTML ); - return sprintf( + return \sprintf( '%s', $urlEvent->getUrl(), StringUtil::specialchars($this->translate('pasteafter.0', $languageDomain)), - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -675,7 +782,11 @@ protected function renderPasteTopButton(EnvironmentInterface $environment, $sort private function breadcrumb(EnvironmentInterface $environment) { $event = new GetBreadcrumbEvent($environment); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); $elements = $event->getElements(); if (empty($elements)) { return null; @@ -723,10 +834,16 @@ private function getSortingColumns($sortingDefinition) */ private function getSelectContainer(EnvironmentInterface $environment) { - $inputProvider = $environment->getInputProvider(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); - $sessionName = $environment->getDataDefinition()->getName() . '.' . $inputProvider->getParameter('mode'); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $sessionName = $definition->getName() . '.' . $inputProvider->getParameter('mode'); if (!$sessionStorage->has($sessionName)) { return []; } @@ -759,7 +876,11 @@ private function handleEditAllButton(CollectionInterface $collection, Environmen } $dataDefinition = $environment->getDataDefinition(); - $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($dataDefinition instanceof ContainerInterface); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + $globalCommands = $backendView->getGlobalCommands(); if (!$globalCommands->hasCommandNamed('all')) { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php index 6cec1b9da..4ff8475e3 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php @@ -32,17 +32,22 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Exception\NotCreatableException; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Event\PostDuplicateModelEvent; use ContaoCommunityAlliance\DcGeneral\Event\PreDuplicateModelEvent; +use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\ActionGuardTrait; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; use ContaoCommunityAlliance\UrlBuilder\Contao\CsrfUrlBuilderFactory; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class CopyModelController handles copy action on a model. @@ -89,7 +94,12 @@ public function handleEvent(ActionEvent $event) } $environment = $event->getEnvironment(); - if (!$environment->getDataDefinition()->getBasicDefinition()->isCreatable()) { + + if (null === ($definition = $environment->getDataDefinition())) { + return; + } + + if (!$definition->getBasicDefinition()->isCreatable()) { return; } @@ -98,7 +108,7 @@ public function handleEvent(ActionEvent $event) } if (true !== ($response = $this->checkPermission($environment))) { - $event->setResponse($response); + $event->setResponse((string) $response); $event->stopPropagation(); return; @@ -123,12 +133,15 @@ public function handleEvent(ActionEvent $event) protected function guardIsCreatable(EnvironmentInterface $environment, ModelIdInterface $modelId, $redirect = false) { $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + if ($dataDefinition->getBasicDefinition()->isCreatable()) { return; } if ($redirect) { $eventDispatcher = $environment->getEventDispatcher(); + assert($eventDispatcher instanceof EventDispatcherInterface); $eventDispatcher->dispatch( new LogEvent( @@ -161,26 +174,44 @@ protected function guardIsCreatable(EnvironmentInterface $environment, ModelIdIn */ public function copy(EnvironmentInterface $environment, ModelIdInterface $modelId) { - $this->guardNotEditOnly($environment->getDataDefinition(), $modelId); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $this->guardNotEditOnly($definition, $modelId); $this->guardIsCreatable($environment, $modelId); $dataProvider = $environment->getDataProvider(); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + + if (!$model) { + throw new DcGeneralRuntimeException( + 'Model not found with ID ' . $modelId->getId() + ); + } + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); // We need to keep the original data here. - $copyModel = $environment->getController()->createClonedModel($model); + $copyModel = $controller->createClonedModel($model); - $eventDispatcher = $environment->getEventDispatcher(); - // Dispatch pre duplicate event. - $preCopyEvent = new PreDuplicateModelEvent($environment, $copyModel, $model); - $eventDispatcher->dispatch($preCopyEvent, $preCopyEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + if (null !== $dispatcher) { + // Dispatch pre duplicate event. + $preCopyEvent = new PreDuplicateModelEvent($environment, $copyModel, $model); + $dispatcher->dispatch($preCopyEvent, $preCopyEvent::NAME); + } // Save the copy. - $environment->getDataProvider($copyModel->getProviderName())->save($copyModel); - - // Dispatch post duplicate event. - $postCopyEvent = new PostDuplicateModelEvent($environment, $copyModel, $model); - $eventDispatcher->dispatch($postCopyEvent, $postCopyEvent::NAME); + $dataProvider = $environment->getDataProvider($copyModel->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $dataProvider->save($copyModel); + + if (null !== $dispatcher) { + // Dispatch post duplicate event. + $postCopyEvent = new PostDuplicateModelEvent($environment, $copyModel, $model); + $dispatcher->dispatch($postCopyEvent, $postCopyEvent::NAME); + } return $copyModel; } @@ -195,18 +226,27 @@ public function copy(EnvironmentInterface $environment, ModelIdInterface $modelI */ protected function redirect($environment, $copiedModelId) { + if (null === ($inputProvider = $environment->getInputProvider())) { + return; + } + // Build a clean url to remove the copy related arguments instead of using the AddToUrlEvent. $urlBuilder = new UrlBuilder(); $urlBuilder ->setPath('contao') - ->setQueryParameter('do', $environment->getInputProvider()->getParameter('do')) + ->setQueryParameter('do', $inputProvider->getParameter('do')) ->setQueryParameter('table', $copiedModelId->getDataProviderName()) ->setQueryParameter('act', 'edit') ->setQueryParameter('id', $copiedModelId->getSerialized()) - ->setQueryParameter('pid', $environment->getInputProvider()->getParameter('pid')); + ->setQueryParameter('pid', $inputProvider->getParameter('pid')); $redirectEvent = new RedirectEvent($this->securityUrlBuilder->create($urlBuilder->getUrl())->getUrl()); - $environment->getEventDispatcher()->dispatch($redirectEvent, ContaoEvents::CONTROLLER_REDIRECT); + + if (null === ($dispatcher = $environment->getEventDispatcher())) { + return; + } + + $dispatcher->dispatch($redirectEvent, ContaoEvents::CONTROLLER_REDIRECT); } /** @@ -214,31 +254,43 @@ protected function redirect($environment, $copiedModelId) * * @param EnvironmentInterface $environment Current dc-general environment. * - * @return string|bool|null + * @return string|false|null */ protected function process(EnvironmentInterface $environment) { - $modelId = ModelId::fromSerialized($environment->getInputProvider()->getParameter('source')); + if (null === ($inputProvider = $environment->getInputProvider())) { + return false; + } - $dataDefinition = $environment->getDataDefinition(); - $this->guardValidEnvironment($dataDefinition, $modelId); + $modelId = ModelId::fromSerialized($inputProvider->getParameter('source')); + + if (null === ($definition = $environment->getDataDefinition())) { + return false; + } + + $this->guardValidEnvironment($definition, $modelId); // We want a redirect here if not creatable. $this->guardIsCreatable($environment, $modelId, true); - if ($dataDefinition->getBasicDefinition()->isEditOnlyMode()) { + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->callAction($environment, 'edit'); } // Manual sorting mode. The ClipboardController should pick it up. $manualSorting = ViewHelpers::getManualSortingProperty($environment); - if ($manualSorting && $environment->getDataProvider()->fieldExists($manualSorting)) { + + if (null === ($provider = $environment->getDataProvider())) { + return false; + } + + if ($manualSorting && $provider->fieldExists($manualSorting)) { return false; } $copiedModel = $this->copy($environment, $modelId); // If edit several don´t redirect do home. - if ('select' === $environment->getInputProvider()->getParameter('act')) { + if ('select' === $inputProvider->getParameter('act')) { return false; } @@ -256,15 +308,23 @@ protected function process(EnvironmentInterface $environment) */ private function checkPermission(EnvironmentInterface $environment) { - if (true === $environment->getDataDefinition()->getBasicDefinition()->isCreatable()) { + if (null === ($definition = $environment->getDataDefinition())) { + return false; + } + + if (true === $definition->getBasicDefinition()->isCreatable()) { return true; } + if (null === ($inputProvider = $environment->getInputProvider())) { + return ''; + } + return \sprintf( '
You have no permission for copy model %s.
', - ModelId::fromSerialized($environment->getInputProvider()->getParameter('source'))->getSerialized() + ModelId::fromSerialized($inputProvider->getParameter('source'))->getSerialized() ); } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php index f5c07f22c..fcbef4e87 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Sven Baumann * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,10 +30,13 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\BaseView; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\EditMask; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\BackCommand; use ContaoCommunityAlliance\DcGeneral\Data\DefaultEditInformation; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; /** * Class CreateHandler @@ -82,8 +85,9 @@ public function handleEvent(ActionEvent $event) $environment = $event->getEnvironment(); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); - // Only handle if we do not have a manual sorting or we know where to insert. + // Only handle if we do not have a manual sorting, or we know where to insert. // Manual sorting is handled by clipboard. if ( ViewHelpers::getManualSortingProperty($environment) @@ -94,13 +98,13 @@ public function handleEvent(ActionEvent $event) } if (true !== ($response = $this->checkPermission($environment))) { - $event->setResponse($response); + $event->setResponse((string) $response); $event->stopPropagation(); return; } - if (false !== ($response = $this->process($environment))) { + if ('' !== ($response = $this->process($environment))) { $event->setResponse($response); } } @@ -115,9 +119,13 @@ public function handleEvent(ActionEvent $event) protected function process(EnvironmentInterface $environment) { $dataProvider = $environment->getDataProvider(); - $properties = $environment->getDataDefinition()->getPropertiesDefinition()->getProperties(); - $model = $dataProvider->getEmptyModel(); - $clone = $dataProvider->getEmptyModel(); + assert($dataProvider instanceof DataProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $properties = $dataDefinition->getPropertiesDefinition()->getProperties(); + $model = $dataProvider->getEmptyModel(); + $clone = $dataProvider->getEmptyModel(); // If some of the fields have a default value, set it. foreach ($properties as $property) { @@ -133,10 +141,13 @@ protected function process(EnvironmentInterface $environment) $view = $environment->getView(); if (!$view instanceof BaseView) { - return false; + return ''; } - if ('select' !== $environment->getInputProvider()->getParameter('act')) { + $provider = $environment->getInputProvider(); + assert($provider instanceof InputProviderInterface); + + if ('select' !== $provider->getParameter('act')) { $this->handleGlobalCommands($environment); } @@ -154,6 +165,7 @@ protected function process(EnvironmentInterface $environment) private function checkPermission(EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); if (true === $dataDefinition->getBasicDefinition()->isCreatable()) { return true; @@ -177,7 +189,11 @@ private function checkPermission(EnvironmentInterface $environment) protected function handleGlobalCommands(EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); - $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($dataDefinition instanceof ContainerInterface); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + $globalCommands = $backendView->getGlobalCommands(); $globalCommands->clearCommands(); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php index f2e74dee4..1e9c56983 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php @@ -31,10 +31,12 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Exception\EditOnlyModeException; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Exception\NotDeletableException; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\DefaultCollection; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DefaultModelRelationshipDefinition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\ParentChildConditionInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; @@ -42,8 +44,10 @@ use ContaoCommunityAlliance\DcGeneral\Event\PostDeleteModelEvent; use ContaoCommunityAlliance\DcGeneral\Event\PreDeleteModelEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\ActionGuardTrait; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class DeleteHandler handles the delete action. @@ -84,7 +88,7 @@ public function handleEvent(ActionEvent $event) } if (true !== ($response = $this->checkPermission($event->getEnvironment()))) { - $event->setResponse($response); + $event->setResponse((string) $response); $event->stopPropagation(); return; @@ -108,6 +112,8 @@ public function handleEvent(ActionEvent $event) protected function guardIsDeletable(EnvironmentInterface $environment, ModelIdInterface $modelId, $redirect = false) { $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + if ($dataDefinition->getBasicDefinition()->isDeletable()) { return; } @@ -117,6 +123,7 @@ protected function guardIsDeletable(EnvironmentInterface $environment, ModelIdIn } $eventDispatcher = $environment->getEventDispatcher(); + assert($eventDispatcher instanceof EventDispatcherInterface); $eventDispatcher->dispatch( new LogEvent( @@ -146,7 +153,9 @@ protected function guardIsDeletable(EnvironmentInterface $environment, ModelIdIn protected function fetchModel(EnvironmentInterface $environment, ModelIdInterface $modelId) { $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); if (!$model || !$model->getId()) { throw new DcGeneralRuntimeException( @@ -171,21 +180,28 @@ protected function fetchModel(EnvironmentInterface $environment, ModelIdInterfac */ public function delete(EnvironmentInterface $environment, ModelIdInterface $modelId) { - $this->guardNotEditOnly($environment->getDataDefinition(), $modelId); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $this->guardNotEditOnly($definition, $modelId); $this->guardIsDeletable($environment, $modelId); $model = $this->fetchModel($environment, $modelId); // Trigger event before the model will be deleted. $preDeleteEvent = new PreDeleteModelEvent($environment, $model); - $environment->getEventDispatcher()->dispatch($preDeleteEvent, $preDeleteEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($preDeleteEvent, $preDeleteEvent::NAME); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); $dataProvider->delete($model); // Trigger event after the model is deleted. $postDeleteEvent = new PostDeleteModelEvent($environment, $model); - $environment->getEventDispatcher()->dispatch($postDeleteEvent, $postDeleteEvent::NAME); + $dispatcher->dispatch($postDeleteEvent, $postDeleteEvent::NAME); } /** @@ -193,16 +209,19 @@ public function delete(EnvironmentInterface $environment, ModelIdInterface $mode * * @param EnvironmentInterface $environment The environment. * - * @return string + * @return string|null */ protected function process(EnvironmentInterface $environment) { - $inputProvider = $environment->getInputProvider(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); - // Guard that we are in the preloaded environment. Otherwise checking the data definition could belong to + // Guard that we are in the preloaded environment. Otherwise, checking the data definition could belong to // another model. $this->guardValidEnvironment($dataDefinition, $modelId); @@ -234,15 +253,21 @@ protected function process(EnvironmentInterface $environment) */ private function checkPermission(EnvironmentInterface $environment) { - if (true === $environment->getDataDefinition()->getBasicDefinition()->isDeletable()) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (true === $definition->getBasicDefinition()->isDeletable()) { return true; } + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + return \sprintf( '
You have no permission for delete model %s.
', - ModelId::fromSerialized($environment->getInputProvider()->getParameter('id'))->getSerialized() + ModelId::fromSerialized($inputProvider->getParameter('id'))->getSerialized() ); } @@ -258,8 +283,11 @@ private function checkPermission(EnvironmentInterface $environment) */ protected function deepDelete(EnvironmentInterface $environment, ModelIdInterface $modelId) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + /** @var DefaultModelRelationshipDefinition $relationships */ - $relationships = $environment->getDataDefinition()->getDefinition('model-relationships'); + $relationships = $definition->getDefinition('model-relationships'); $childConditions = $relationships->getChildConditions($modelId->getDataProviderName()); @@ -271,11 +299,15 @@ protected function deepDelete(EnvironmentInterface $environment, ModelIdInterfac continue; } - $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch( - $dataProvider->getEmptyConfig()->setId($modelId->getId()) - ); + $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($model instanceof ModelInterface); + $destinationChildDataProvider = $environment->getDataProvider($childCondition->getDestinationName()); + assert($destinationChildDataProvider instanceof DataProviderInterface); + /** @var DefaultCollection $destinationChildModels */ $destinationChildModels = $destinationChildDataProvider->fetchAll( @@ -291,9 +323,14 @@ protected function deepDelete(EnvironmentInterface $environment, ModelIdInterfac } foreach ($childConditions as $childCondition) { - $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($model instanceof ModelInterface); + $childDataProvider = $environment->getDataProvider($childCondition->getDestinationName()); + assert($childDataProvider instanceof DataProviderInterface); $filters = $childCondition->getFilter($model); /** @var DefaultCollection $childModels */ @@ -303,15 +340,19 @@ protected function deepDelete(EnvironmentInterface $environment, ModelIdInterfac } foreach ($childModels as $childModel) { + if (null === ($dispatcher = $environment->getEventDispatcher())) { + continue; + } + // Trigger event before the model will be deleted. $preDeleteEvent = new PreDeleteModelEvent($environment, $childModel); - $environment->getEventDispatcher()->dispatch($preDeleteEvent, $preDeleteEvent::NAME); + $dispatcher->dispatch($preDeleteEvent, $preDeleteEvent::NAME); $childDataProvider->delete($childModel); // Trigger event after the model is deleted. $postDeleteEvent = new PostDeleteModelEvent($environment, $childModel); - $environment->getEventDispatcher()->dispatch($postDeleteEvent, $postDeleteEvent::NAME); + $dispatcher->dispatch($postDeleteEvent, $postDeleteEvent::NAME); } } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php index 7751ae739..4d9f61a05 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php @@ -30,12 +30,17 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\BaseView; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\EditMask; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\BackCommand; use ContaoCommunityAlliance\DcGeneral\Data\DefaultEditInformation; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class CreateHandler @@ -100,15 +105,18 @@ public function handleEvent(ActionEvent $event) * * @param EnvironmentInterface $environment The environment. * - * @return string|bool + * @return string|false * * @throws DcGeneralRuntimeException When the requested model could not be located in the database. */ protected function process(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); - $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); - $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($inputProvider instanceof InputProviderInterface); + + $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); + $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); $view = $environment->getView(); if (!$view instanceof BaseView) { @@ -143,11 +151,15 @@ private function checkPermission(ActionEvent $event) { $environment = $event->getEnvironment(); - if (true === $environment->getDataDefinition()->getBasicDefinition()->isEditable()) { + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if (true === $dataDefinition->getBasicDefinition()->isEditable()) { return true; } $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $event->setResponse( \sprintf( @@ -168,7 +180,7 @@ private function checkPermission(ActionEvent $event) * reload of the page. * * @param EnvironmentInterface $environment The environment. - * @param ModelId $modelId The model id. + * @param ModelIdInterface $modelId The model id. * * @return void * @@ -176,12 +188,18 @@ private function checkPermission(ActionEvent $event) * * @SuppressWarnings(PHPMD.LongVariable) */ - private function checkRestoreVersion(EnvironmentInterface $environment, ModelId $modelId) + private function checkRestoreVersion(EnvironmentInterface $environment, ModelIdInterface $modelId) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $dataProviderDefinition = $dataDefinition->getDataProviderDefinition(); - $dataProviderDefinition = $environment->getDataDefinition()->getDataProviderDefinition(); - $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); if ( !((null !== ($modelVersion = $inputProvider->getValue('version'))) @@ -191,6 +209,9 @@ private function checkRestoreVersion(EnvironmentInterface $environment, ModelId return; } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + if (null === ($model = $dataProvider->getVersion($modelId->getId(), $modelVersion))) { $message = \sprintf( 'Could not load version %s of record ID %s from %s', @@ -199,7 +220,7 @@ private function checkRestoreVersion(EnvironmentInterface $environment, ModelId $modelId->getDataProviderName() ); - $environment->getEventDispatcher()->dispatch( + $dispatcher->dispatch( new LogEvent($message, 'ERROR', 'DC_General - checkRestoreVersion()'), ContaoEvents::SYSTEM_LOG ); @@ -209,7 +230,7 @@ private function checkRestoreVersion(EnvironmentInterface $environment, ModelId $dataProvider->save($model); $dataProvider->setVersionActive($modelId->getId(), $modelVersion); - $environment->getEventDispatcher()->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); + $dispatcher->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); } /** @@ -221,7 +242,12 @@ private function checkRestoreVersion(EnvironmentInterface $environment, ModelId */ protected function handleGlobalCommands(EnvironmentInterface $environment) { - $backendView = $environment->getDataDefinition()->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + $globalCommands = $backendView->getGlobalCommands(); $globalCommands->clearCommands(); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php index 147c86086..8f1286e92 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author David Molineus - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,8 +25,10 @@ use ContaoCommunityAlliance\DcGeneral\Action; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ListingConfigInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; /** @@ -65,9 +68,16 @@ protected function determineTemplate($groupingInformation) */ protected function renderTemplate(ContaoBackendViewTemplate $template, EnvironmentInterface $environment) { - $dataDefinition = $environment->getDataDefinition(); - $viewDefinition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); - $groupAndSorting = $viewDefinition->getListingConfig()->getGroupAndSortingDefinition(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $viewDefinition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($viewDefinition instanceof Contao2BackendViewDefinitionInterface); + + $listingConfig = $viewDefinition->getListingConfig(); + assert($listingConfig instanceof ListingConfigInterface); + + $groupAndSorting = $listingConfig->getGroupAndSortingDefinition(); $pasteButton = $this->renderPasteTopButton($environment, $groupAndSorting); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php index 995c09c3e..bad69dbf4 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -26,16 +27,20 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoWidgetManager; use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\EditInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\AbstractPropertyOverrideEditAllHandler; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * The class handle the "editAll" commands. @@ -60,7 +65,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) /** * {@inheritDoc} */ - public function handleEvent(ActionEvent $event) + public function handleEvent(ActionEvent $event): void { if ( !$this->getScopeDeterminator()->currentScopeIsBackend() @@ -69,19 +74,21 @@ public function handleEvent(ActionEvent $event) return; } - if (false !== ($response = $this->process($event->getAction(), $event->getEnvironment()))) { - $event->setResponse($response); - $event->stopPropagation(); - } + $response = $this->process($event->getAction(), $event->getEnvironment()); + $event->setResponse($response); + $event->stopPropagation(); } /** * {@inheritDoc} */ - private function process(Action $action, EnvironmentInterface $environment) + private function process(Action $action, EnvironmentInterface $environment): string { $inputProvider = $environment->getInputProvider(); - $translator = $environment->getTranslator(); + assert($inputProvider instanceof InputProviderInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $renderInformation = new \ArrayObject(); @@ -93,6 +100,9 @@ private function process(Action $action, EnvironmentInterface $environment) $this->handleSubmit($action, $environment); } + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + return $this->renderTemplate( $action, [ @@ -100,7 +110,7 @@ private function process(Action $action, EnvironmentInterface $environment) $translator->translate('MSC.' . $inputProvider->getParameter('mode') . 'Selected') . ': ' . $translator->translate('MSC.all.0'), 'fieldsets' => $renderInformation->offsetGet('fieldsets'), - 'table' => $environment->getDataDefinition()->getName(), + 'table' => $definition->getName(), 'error' => $renderInformation->offsetGet('error'), 'breadcrumb' => $this->renderBreadcrumb($environment), 'editButtons' => $this->getEditButtons($action, $environment), @@ -122,16 +132,22 @@ private function process(Action $action, EnvironmentInterface $environment) */ private function buildFieldSets(Action $action, \ArrayObject $renderInformation, EnvironmentInterface $environment) { - $formInputs = $environment->getInputProvider()->getValue('FORM_INPUTS'); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $formInputs = $inputProvider->getValue('FORM_INPUTS'); $collection = $this->getCollectionFromSession($action, $environment); $fieldSets = []; $errors = []; while ($collection->count() > 0) { - $model = $collection->shift(); - $modelId = ModelId::fromModel($model); + if (null === ($model = $collection->shift())) { + continue; + } + $modelId = ModelId::fromModel($model); $propertyValuesBag = $this->getPropertyValueBagFromModel($action, $model, $environment); + if ($formInputs) { $this->handleEditCollection($action, $propertyValuesBag, $model, $renderInformation, $environment); } @@ -173,6 +189,8 @@ private function buildFieldSets(Action $action, \ArrayObject $renderInformation, private function handleLegendCollapsed(array $fieldSets) { $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); + if (!$editInformation->hasAnyModelError()) { return $fieldSets; } @@ -208,13 +226,20 @@ private function renderEditFields( PropertyValueBagInterface $propertyValuesBag, EnvironmentInterface $environment ) { - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); - $selectProperties = (array) $this->getPropertiesFromSession($action, $environment); + $selectProperties = $this->getPropertiesFromSession($action, $environment); $modelId = ModelId::fromModel($model); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $editModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + + $editModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($editModel instanceof ModelInterface); + $visibleModel = $this->getVisibleModel($action, $editModel, $dataProvider, $environment); $fields = []; @@ -259,9 +284,12 @@ private function renderEditFields( } if (null === $fields[0]) { + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $fields[] = \sprintf( '

 

%s

 

', - $environment->getTranslator()->translate('MSC.no_properties_available') + $translator->translate('MSC.no_properties_available') ); } @@ -284,7 +312,7 @@ private function getVisibleModel( DataProviderInterface $dataProvider, EnvironmentInterface $environment ) { - $selectProperties = (array) $this->getPropertiesFromSession($action, $environment); + $selectProperties = $this->getPropertiesFromSession($action, $environment); $visibleModel = $dataProvider->getEmptyModel(); $visibleModel->setId($editModel->getId()); @@ -382,7 +410,9 @@ private function markModelErrors( EnvironmentInterface $environment ) { $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); - $sessionValues = $this->getEditPropertiesByModelId($action, ModelId::fromModel($model), $environment); + assert($editInformation instanceof EditInformationInterface); + + $sessionValues = $this->getEditPropertiesByModelId($action, ModelId::fromModel($model), $environment); $modelError = $editInformation->getModelError($editModel); if ($modelError && isset($modelError[$selectProperty->getName()])) { @@ -421,7 +451,9 @@ private function handleEditCollection( \ArrayObject $renderInformation, EnvironmentInterface $environment ) { - $dataProvider = $environment->getDataProvider($model->getProviderName()); + $dataProvider = $environment->getDataProvider($model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $editCollection = $dataProvider->getEmptyCollection(); $revertCollection = $dataProvider->getEmptyCollection(); @@ -458,7 +490,7 @@ private function buildEditProperty(PropertyInterface $originalProperty, ModelIdI $editProperty->setSearchable($originalProperty->isSearchable()); $editProperty->setFilterable($originalProperty->isFilterable()); $editProperty->setWidgetType($originalProperty->getWidgetType()); - $editProperty->setOptions($originalProperty->getOptions()); + $editProperty->setOptions($originalProperty->getOptions() ?? []); $editProperty->setExplanation($originalProperty->getExplanation()); $editProperty->setExtra($originalProperty->getExtra()); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php index c438ae8de..b70cced3f 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -23,20 +24,29 @@ use Contao\System; use Contao\Widget; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoWidgetManager; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Widget\AbstractWidget; +use ContaoCommunityAlliance\DcGeneral\Data\EditInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\AbstractPropertyOverrideEditAllHandler; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * The class handle the "overrideAll" commands. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class OverrideAllHandler extends AbstractPropertyOverrideEditAllHandler { @@ -56,7 +66,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) /** * {@inheritDoc} */ - public function handleEvent(ActionEvent $event) + public function handleEvent(ActionEvent $event): void { if ( !$this->getScopeDeterminator()->currentScopeIsBackend() @@ -65,7 +75,7 @@ public function handleEvent(ActionEvent $event) return; } - if (false !== ($response = $this->process($event->getAction(), $event->getEnvironment()))) { + if ('' !== ($response = $this->process($event->getAction(), $event->getEnvironment()))) { $event->setResponse($response); $event->stopPropagation(); } @@ -83,9 +93,14 @@ public function handleEvent(ActionEvent $event) */ private function process(Action $action, EnvironmentInterface $environment) { - $inputProvider = $environment->getInputProvider(); - $translator = $environment->getTranslator(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); $renderInformation = new \ArrayObject(); @@ -109,6 +124,9 @@ private function process(Action $action, EnvironmentInterface $environment) $this->handleSubmit($action, $environment); } + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + return $this->renderTemplate( $action, [ @@ -116,11 +134,11 @@ private function process(Action $action, EnvironmentInterface $environment) $translator->translate('MSC.' . $inputProvider->getParameter('mode') . 'Selected') . ': ' . $translator->translate('MSC.all.0'), 'fieldsets' => $renderInformation->offsetGet('fieldsets'), - 'table' => $environment->getDataDefinition()->getName(), + 'table' => $definition->getName(), 'error' => $renderInformation->offsetGet('error'), 'breadcrumb' => $this->renderBreadcrumb($environment), 'editButtons' => $this->getEditButtons($action, $environment), - 'noReload' => (bool) $editInformation->hasAnyModelError() + 'noReload' => $editInformation->hasAnyModelError() ] ); } @@ -152,6 +170,7 @@ protected function handleInvalidPropertyValueBag( } $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); foreach (\array_keys($propertyValueBag->getArrayCopy()) as $propertyName) { $allErrors = $propertyValueBag->getPropertyValueErrors($propertyName); @@ -172,7 +191,11 @@ protected function handleInvalidPropertyValueBag( $event = new EncodePropertyValueFromWidgetEvent($environment, $model, $eventPropertyValueBag); $event->setProperty($propertyName) ->setValue($inputProvider->getValue($propertyName, true)); - $environment->getEventDispatcher()->dispatch($event, EncodePropertyValueFromWidgetEvent::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, EncodePropertyValueFromWidgetEvent::NAME); $propertyValueBag->setPropertyValue($propertyName, $event->getValue()); @@ -239,17 +262,17 @@ private function getOverrideProperties(Action $action, EnvironmentInterface $env /** * Render the field sets. * - * @param Action $action The action. - * @param \ArrayObject $renderInformation The render information. - * @param PropertyValueBagInterface|null $propertyValues The property values. - * @param EnvironmentInterface $environment The environment. + * @param Action $action The action. + * @param \ArrayObject $renderInformation The render information. + * @param PropertyValueBagInterface $propertyValues The property values. + * @param EnvironmentInterface $environment The environment. * * @return void */ private function renderFieldSets( Action $action, \ArrayObject $renderInformation, - PropertyValueBagInterface $propertyValues = null, + PropertyValueBagInterface $propertyValues, EnvironmentInterface $environment ) { $properties = $this->getOverrideProperties($action, $environment); @@ -260,7 +283,7 @@ private function renderFieldSets( $errors = []; $fieldSet = ['palette' => '', 'class' => 'tl_box']; - $propertyNames = $propertyValues ? \array_keys($propertyValues->getArrayCopy()) : \array_keys($properties); + $propertyNames = \array_keys($propertyValues->getArrayCopy()); foreach ($propertyNames as $propertyName) { $errors = $this->getPropertyValueErrors($propertyValues, $propertyName, $errors); @@ -274,12 +297,13 @@ private function renderFieldSets( $this->setDefaultValue($model, $propertyValues, $propertyName, $environment); $widget = $widgetManager->getWidget($property->getName(), $propertyValues); + assert($widget instanceof Widget); $widgetModel = $this->getModelFromWidget($widget); if (!$this->ensurePropertyVisibleInModel($action, $property->getName(), $widgetModel, $environment)) { $fieldSet['palette'] .= - $this->injectSelectParentPropertyInformation($action, $property, $widgetModel, $environment); + $this->injectSelectParentPropertyInformation($action, $property, $widgetModel, $environment) ?? ''; continue; } @@ -299,13 +323,16 @@ private function renderFieldSets( $widgetModel, $propertyValues, $environment - ); + ) ?? ''; } + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + if (empty($fieldSet['palette'])) { $fieldSet['palette'] = \sprintf( '

 

%s

 

', - $environment->getTranslator()->translate('MSC.no_properties_available') + $translator->translate('MSC.no_properties_available') ); } @@ -320,13 +347,16 @@ private function renderFieldSets( * * @return ModelInterface */ - private function getModelFromWidget(Widget $widget) + private function getModelFromWidget(Widget $widget): ModelInterface { - if ($widget->dataContainer) { + if ($widget->dataContainer instanceof DcCompat) { return $widget->dataContainer->getModel(); } + if ($widget instanceof AbstractWidget) { + return $widget->getModel(); + } - return $widget->getModel(); + throw new \InvalidArgumentException('Expected an instance of ' . AbstractWidget::class); } /** @@ -338,11 +368,13 @@ private function getModelFromWidget(Widget $widget) * * @return array */ - private function getPropertyValueErrors(PropertyValueBagInterface $propertyValueBag, $propertyName, array $errors) - { + private function getPropertyValueErrors( + PropertyValueBagInterface $propertyValueBag, + string $propertyName, + array $errors + ): array { if ( - (null !== $propertyValueBag) - && $propertyValueBag->hasPropertyValue($propertyName) + $propertyValueBag->hasPropertyValue($propertyName) && $propertyValueBag->isPropertyValueInvalid($propertyName) ) { $errors = \array_merge( @@ -367,10 +399,13 @@ private function getPropertyValueErrors(PropertyValueBagInterface $propertyValue private function setDefaultValue( ModelInterface $model, PropertyValueBagInterface $propertyValueBag, - $propertyName, + string $propertyName, EnvironmentInterface $environment - ) { - $propertiesDefinition = $environment->getDataDefinition()->getPropertiesDefinition(); + ): void { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $propertiesDefinition = $definition->getPropertiesDefinition(); // If in the intersect model the value available, then set it as default. if ($modelValue = $model->getProperty($propertyName)) { @@ -381,7 +416,8 @@ private function setDefaultValue( if ( $propertiesDefinition->hasProperty($propertyName) - && !$environment->getInputProvider()->hasValue($propertyName) + && null !== ($inputProvider = $environment->getInputProvider()) + && !$inputProvider->hasValue($propertyName) ) { $propertyValueBag->setPropertyValue( $propertyName, diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php index 3b34d451d..ef26626b2 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php @@ -20,24 +20,33 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ActionHandler\MultipleHandler; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\ItemInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Event\PostDuplicateModelEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Action handler for paste all action. * * @return void + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PasteAllHandler { @@ -47,16 +56,16 @@ class PasteAllHandler /** * The copied model is available by paste mode copy. * - * @var ModelIdInterface + * @var ModelInterface|null */ - protected $copiedModel; + protected $copiedModel = null; /** * The original model is available by paste mode copy. * - * @var ModelIdInterface + * @var ModelInterface|null */ - protected $originalModel; + protected $originalModel = null; /** @@ -72,7 +81,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) /** * {@inheritDoc} */ - public function handleEvent(ActionEvent $event) + public function handleEvent(ActionEvent $event): void { if ( !$this->getScopeDeterminator()->currentScopeIsBackend() @@ -101,7 +110,10 @@ private function process(EnvironmentInterface $environment) } $inputProvider = $environment->getInputProvider(); - $clipboard = $environment->getClipboard(); + assert($inputProvider instanceof InputProviderInterface); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); $inputProvider->setParameter('pasteAll', true); @@ -131,17 +143,30 @@ private function process(EnvironmentInterface $environment) */ protected function getClipboardItems(EnvironmentInterface $environment) { - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + + $provider = $basicDefinition->getDataProvider(); + assert(\is_string($provider)); + + $parentProvider = $basicDefinition->getParentDataProvider(); + assert(\is_string($parentProvider)); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + $filter->andModelIsFromProvider($provider); if ($basicDefinition->getParentDataProvider()) { - $filter->andParentIsFromProvider($basicDefinition->getParentDataProvider()); + $filter->andParentIsFromProvider($parentProvider); } else { $filter->andHasNoParent(); } - return $environment->getClipboard()->fetch($filter); + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + return $clipboard->fetch($filter); } /** @@ -154,6 +179,8 @@ protected function getClipboardItems(EnvironmentInterface $environment) protected function getCollection(EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $relationShip = $dataDefinition->getModelRelationshipDefinition(); if (!$relationShip->getChildCondition($dataDefinition->getName(), $dataDefinition->getName())) { @@ -173,6 +200,7 @@ protected function getCollection(EnvironmentInterface $environment) protected function getFlatCollection(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $previousItem = null; $collection = []; @@ -207,9 +235,15 @@ protected function getFlatCollection(EnvironmentInterface $environment) */ protected function getHierarchyCollection(array $clipboardItems, EnvironmentInterface $environment) { - $dataProvider = $environment->getDataProvider(); + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $relationShip = $dataDefinition->getModelRelationshipDefinition(); $childCondition = $relationShip->getChildCondition($dataDefinition->getName(), $dataDefinition->getName()); if (null === $childCondition) { @@ -241,19 +275,19 @@ protected function getHierarchyCollection(array $clipboardItems, EnvironmentInte $previousItem = $clipboardItem; - $model = - $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($model instanceof ModelInterface); $itemCollection = $dataProvider->fetchAll($dataProvider->getEmptyConfig()->setFilter($childCondition->getFilter($model))); - if ($itemCollection) { - $collection = $this->setSubItemsToCollection( - $clipboardItem, - $this->getSubClipboardItems($clipboardItems, $itemCollection), - $collection, - $environment - ); - } + assert($itemCollection instanceof CollectionInterface); + + $collection = $this->setSubItemsToCollection( + $clipboardItem, + $this->getSubClipboardItems($clipboardItems, $itemCollection), + $collection, + $environment + ); } return $collection; @@ -307,7 +341,11 @@ protected function setSubItemsToCollection( } $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $relationShip = $dataDefinition->getModelRelationshipDefinition(); $childCondition = $relationShip->getChildCondition($dataDefinition->getName(), $dataDefinition->getName()); if (null === $childCondition) { @@ -338,19 +376,19 @@ protected function setSubItemsToCollection( 'pasteMode' => $intoItem ? 'after' : 'into' ]; - $model = - $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($model instanceof ModelInterface); + $itemCollection = $dataProvider->fetchAll($dataProvider->getEmptyConfig()->setFilter($childCondition->getFilter($model))); + assert($itemCollection instanceof CollectionInterface); - if ($itemCollection) { - $collection = $this->setSubItemsToCollection( - $subClipboardItem, - $this->getSubClipboardItems($this->getClipboardItems($environment), $itemCollection), - $collection, - $environment - ); - } + $collection = $this->setSubItemsToCollection( + $subClipboardItem, + $this->getSubClipboardItems($this->getClipboardItems($environment), $itemCollection), + $collection, + $environment + ); } return $collection; @@ -365,7 +403,10 @@ protected function setSubItemsToCollection( */ protected function addDispatchDuplicateModel(EnvironmentInterface $environment) { - $environment->getEventDispatcher()->addListener( + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->addListener( PostDuplicateModelEvent::NAME, function (PostDuplicateModelEvent $event) { $this->copiedModel = $event->getModel(); @@ -385,6 +426,8 @@ function (PostDuplicateModelEvent $event) { protected function setParameterForPaste(array $collectionItem, EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $clipboardItem = $collectionItem['item']; $inputProvider->unsetParameter('after'); @@ -405,6 +448,7 @@ protected function setParameterForPaste(array $collectionItem, EnvironmentInterf return; } + assert($this->copiedModel instanceof ModelInterface); $copiedModelId = ModelId::fromModel($this->copiedModel); $inputProvider->setParameter($collectionItem['pasteMode'], $copiedModelId->getSerialized()); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php index 56b22af9b..92d5a52dd 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,7 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -24,6 +24,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; /** @@ -60,7 +61,7 @@ public function handleEvent(ActionEvent $event) return; } - if (false !== ($response = $this->process($event->getAction(), $event->getEnvironment()))) { + if ('' !== ($response = $this->process($event->getAction(), $event->getEnvironment()))) { $event->setResponse($response); } } @@ -76,6 +77,7 @@ public function handleEvent(ActionEvent $event) private function process(Action $action, EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); return $this->callAction( $environment, @@ -87,6 +89,6 @@ private function process(Action $action, EnvironmentInterface $environment) 'select' => $inputProvider->getParameter('select') ] ) - ); + ) ?? ''; } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php index acbdfcc6f..6d10da9c5 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,7 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -31,6 +31,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\NoOpDataProvider; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\DefaultProperty; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; @@ -38,6 +39,9 @@ use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * This class handles the rendering of list view "showAllProperties" actions. @@ -64,11 +68,10 @@ public function handleEvent(ActionEvent $event) !$this->getScopeDeterminator()->currentScopeIsBackend() || ('selectPropertyAll' !== $event->getAction()->getName()) ) { - return null; + return; } - $response = $this->process($event->getAction(), $event->getEnvironment()); - if (false !== $response) { + if (null !== $response = $this->process($event->getAction(), $event->getEnvironment())) { $event->setResponse($response); } } @@ -79,10 +82,13 @@ public function handleEvent(ActionEvent $event) protected function process(Action $action, EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); - $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($dataDefinition instanceof ContainerInterface); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); $backendView->getListingConfig()->setShowColumns(false); - $environment->getDataDefinition()->getBasicDefinition()->setMode(BasicDefinitionInterface::MODE_FLAT); + $dataDefinition->getBasicDefinition()->setMode(BasicDefinitionInterface::MODE_FLAT); return parent::process($action, $environment); } @@ -112,7 +118,10 @@ protected function loadCollection(EnvironmentInterface $environment) */ private function getPropertyDataProvider(EnvironmentInterface $environment) { - $providerName = 'property.' . $environment->getDataDefinition()->getName(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $providerName = 'property.' . $definition->getName(); $dataProvider = new NoOpDataProvider(); $dataProvider->setBaseConfig(['name' => $providerName]); @@ -132,13 +141,19 @@ private function getPropertyDataProvider(EnvironmentInterface $environment) */ private function setPropertyLabelFormatter($providerName, EnvironmentInterface $environment) { - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); $labelFormatter = (new DefaultModelFormatterConfig()) ->setPropertyNames(['name', 'description']) ->setFormat('%s [%s]'); - $this->getViewSection($environment->getDataDefinition()) + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $this->getViewSection($definition) ->getListingConfig() ->setLabelFormatter($providerName, $labelFormatter); @@ -154,7 +169,7 @@ private function setPropertyLabelFormatter($providerName, EnvironmentInterface $ } /** - * Return the field collection for each properties. + * Return the field collection for each property. * * @param DataProviderInterface $dataProvider The field data provider. * @param EnvironmentInterface $environment The environment. @@ -165,7 +180,10 @@ private function getCollection(DataProviderInterface $dataProvider, EnvironmentI { $collection = $dataProvider->getEmptyCollection(); - foreach ($environment->getDataDefinition()->getPropertiesDefinition() as $property) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + foreach ($definition->getPropertiesDefinition() as $property) { if (!$this->isPropertyAllowed($property, $environment)) { continue; } @@ -205,17 +223,21 @@ private function isPropertyAllowed(PropertyInterface $property, EnvironmentInter } $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); - $extra = (array) $property->getExtra(); + $extra = $property->getExtra(); if ( $this->isPropertyAllowedByEdit($extra, $environment) || $this->isPropertyAllowedByOverride($extra, $environment) ) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + Message::addInfo( \sprintf( $translator->translate('MSC.not_allowed_property_info'), $property->getLabel() ?: $property->getName(), - $translator->translate('MSC.' . $environment->getInputProvider()->getParameter('mode') . 'Selected') + $translator->translate('MSC.' . $inputProvider->getParameter('mode') . 'Selected') ) ); @@ -235,8 +257,11 @@ private function isPropertyAllowed(PropertyInterface $property, EnvironmentInter */ private function isPropertyAllowedByEdit(array $extra, EnvironmentInterface $environment) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + return (true === ($extra['doNotEditMultiple'] ?? false)) - && ('edit' === $environment->getInputProvider()->getParameter('mode')); + && ('edit' === $inputProvider->getParameter('mode')); } /** @@ -249,10 +274,13 @@ private function isPropertyAllowedByEdit(array $extra, EnvironmentInterface $env */ private function isPropertyAllowedByOverride(array $extra, EnvironmentInterface $environment) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + return ((true === ($extra['unique'] ?? false)) || isset($extra['readonly']) || (true === ($extra['doNotOverrideMultiple'] ?? false))) - && ('override' === $environment->getInputProvider()->getParameter('mode')); + && ('override' === $inputProvider->getParameter('mode')); } /** @@ -268,8 +296,13 @@ private function isPropertyAllowedByIntersectProperties( EnvironmentInterface $environment ) { $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $inputProvider->getParameter('mode')); @@ -341,8 +374,11 @@ private function handlePropertyFileTreeOrder(PropertyInterface $property, ModelI */ protected function renderTemplate(ContaoBackendViewTemplate $template, EnvironmentInterface $environment) { - $inputProvider = $environment->getInputProvider(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); $languageDomain = 'contao_' . $dataDefinition->getName(); @@ -388,7 +424,10 @@ protected function renderTemplate(ContaoBackendViewTemplate $template, Environme */ protected function getSelectButtons(EnvironmentInterface $environment) { - $languageDomain = 'contao_' . $environment->getDataDefinition()->getName(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $languageDomain = 'contao_' . $definition->getName(); $confirmMessage = \htmlentities( \sprintf( @@ -403,29 +442,34 @@ protected function getSelectButtons(EnvironmentInterface $environment) 'BackendGeneral.hideMessage(); return false;' ) ); - $onClick = 'BackendGeneral.confirmSelectOverrideEditAll(this, \'properties[]\', \'' . - $confirmMessage . '\'); return false;'; - - $continueName = $environment->getInputProvider()->getParameter('mode'); - $buttons['continue'] = \sprintf( - '', - $continueName, - $continueName, - 'c', - StringUtil::specialchars($this->translate('MSC.continue', $languageDomain)), - $onClick - ); - return $buttons; + $onClick = 'BackendGeneral.confirmSelectOverrideEditAll(this, \'properties[]\', \'' . + $confirmMessage . '\'); return false;'; + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $continueName = $inputProvider->getParameter('mode'); + + return [ + 'continue' => \sprintf( + '', + $continueName, + $continueName, + 'c', + StringUtil::specialchars($this->translate('MSC.continue', $languageDomain)), + $onClick + ) + ]; } /** * Check if the action should be handled. * - * @param string $mode The list mode. + * @param int $mode The list mode. * @param Action $action The action. * - * @return mixed + * @return bool */ protected function wantToHandle($mode, Action $action) { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php index 6cfd40914..5a7f00b77 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author David Molineus * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,13 +31,18 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\Date\ParseDateEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\ItemInterface; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\BackendViewInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetParentHeaderEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ParentViewChildRecordEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; @@ -47,6 +52,10 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; +use ContaoCommunityAlliance\DcGeneral\View\ViewInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class handles the rendering of parented list view "showAll" actions. @@ -72,19 +81,20 @@ protected function wantToHandle($mode, Action $action) protected function renderModel(ModelInterface $model, EnvironmentInterface $environment) { $event = new ParentViewChildRecordEvent($environment, $model); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); - - if (null !== $event->getHtml()) { - $information = [ - [ - 'colspan' => 1, - 'class' => 'tl_file_list col_1', - 'content' => $event->getHtml() - ] - ]; - $model->setMeta($model::LABEL_VALUE, $information); - return; - } + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); + + $information = [ + [ + 'colspan' => 1, + 'class' => 'tl_file_list col_1', + 'content' => $event->getHtml() + ] + ]; + $model->setMeta($model::LABEL_VALUE, $information); parent::renderModel($model, $environment); } @@ -129,7 +139,10 @@ protected function renderTemplate(ContaoBackendViewTemplate $template, Environme */ protected function loadParentModel(EnvironmentInterface $environment) { - $pidDetails = ModelId::fromSerialized($environment->getInputProvider()->getParameter('pid')); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $pidDetails = ModelId::fromSerialized($inputProvider->getParameter('pid')); if (!($provider = $environment->getDataProvider($pidDetails->getDataProviderName()))) { throw new DcGeneralRuntimeException( @@ -160,9 +173,17 @@ protected function loadParentModel(EnvironmentInterface $environment) private function renderHeaderFields($parentModel, EnvironmentInterface $environment) { $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $parentName = $definition->getBasicDefinition()->getParentDataProvider(); - $add = []; - $properties = $environment->getParentDataDefinition()->getPropertiesDefinition(); + assert(\is_string($parentName)); + + $add = []; + + $parentDefinition = $environment->getParentDataDefinition(); + assert($parentDefinition instanceof ContainerInterface); + + $properties = $parentDefinition->getPropertiesDefinition(); foreach ($this->getViewSection($definition)->getListingConfig()->getHeaderPropertyNames() as $field) { $value = StringUtil::deserialize($parentModel->getProperty($field)); @@ -181,9 +202,12 @@ private function renderHeaderFields($parentModel, EnvironmentInterface $environm $event = new GetParentHeaderEvent($environment, $parentModel); $event->setAdditional($add); - $environment->getEventDispatcher()->dispatch($event, GetParentHeaderEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, GetParentHeaderEvent::NAME); - if (null !== !$event->getAdditional()) { + if ($event->getAdditional()) { $add = $event->getAdditional(); } @@ -204,7 +228,7 @@ function ($value) { * @param string $field The field name. * @param string $parentName The parent definition name. * - * @return array|string + * @return string */ private function translateHeaderColumnName($field, $parentName) { @@ -239,11 +263,11 @@ private function renderParentProperty(EnvironmentInterface $environment, $proper : $value; $options = $property->getOptions(); - if ((isset($evaluation['isAssociative']) && $evaluation['isAssociative']) || ArrayUtil::isAssoc($options)) { + if (\is_array($options) && (($evaluation['isAssociative'] ?? false) || ArrayUtil::isAssoc($options))) { $value = $options[$value]; } - return $value; + return $value ?? ''; } /** @@ -304,7 +328,11 @@ private function renderForDateTime( $isRendered = true; $event = new ParseDateEvent($value, Config::get($evaluation['rgxp'] . 'Format')); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::DATE_PARSE); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::DATE_PARSE); return $event->getResult(); } @@ -342,12 +370,25 @@ private function renderReference($value, $reference, &$isRendered) */ private function getParentModelButtons($parentModel, EnvironmentInterface $environment) { - if ('select' === $environment->getInputProvider()->getParameter('act')) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if ('select' === $inputProvider->getParameter('act')) { return ''; } - $config = $environment->getBaseConfigRegistry()->getBaseConfig(); - $environment->getView()->getPanel()->initialize($config); + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $view = $environment->getView(); + assert($view instanceof BackendViewInterface); + + $panel = $view->getPanel(); + assert($panel instanceof PanelContainerInterface); + + $config = $registry->getBaseConfig(); + + $panel->initialize($config); if (!$config->getSorting()) { return ''; } @@ -373,23 +414,33 @@ private function getParentModelButtons($parentModel, EnvironmentInterface $envir protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentInterface $environment) { $parentDefinition = $environment->getParentDataDefinition(); + assert($parentDefinition instanceof ContainerInterface); + + $backendView = $parentDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + /** @var CommandCollectionInterface $commands */ - $commands = $parentDefinition - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME) - ->getModelCommands(); + $commands = $backendView->getModelCommands(); if (!$commands->hasCommandNamed('edit') || !$parentDefinition->getBasicDefinition()->isEditable()) { return null; } - $parentName = $environment->getDataDefinition()->getBasicDefinition()->getParentDataProvider(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $parentName = $definition->getBasicDefinition()->getParentDataProvider(); $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $command = $commands->getCommandNamed('edit'); $parameters = (array) $command->getParameters(); // This should be set in command builder rather than here. - $parameters['do'] = $environment->getInputProvider()->getParameter('do'); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $parameters['do'] = $inputProvider->getParameter('do'); $parameters['table'] = $parentName; $parameters['pid'] = ''; @@ -418,7 +469,7 @@ protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentI $href = ''; foreach ($parameters as $key => $value) { - $href .= \sprintf('&%s=%s', $key, $value); + $href .= \sprintf('&%s=%s', $key, $value ?? ''); } /** @var AddToUrlEvent $urlAfter */ $urlAfter = $dispatcher->dispatch(new AddToUrlEvent($href), ContaoEvents::BACKEND_ADD_TO_URL); @@ -429,7 +480,7 @@ protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentI StringUtil::specialchars( \sprintf($this->translate('editheader.1', $parentDefinition->getName()), $parentModel->getId()) ), - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -443,23 +494,36 @@ protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentI */ private function getHeaderPasteNewButton(ModelInterface $parentModel, EnvironmentInterface $environment) { - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + if (!$basicDefinition->isCreatable()) { return null; } + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + $filter->andModelIsFromProvider($dataProvider); if ($parentProviderName = $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } - if ($environment->getClipboard()->isNotEmpty($filter)) { + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + if ($clipboard->isNotEmpty($filter)) { return null; } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); /** @var AddToUrlEvent $urlEvent */ $urlEvent = $dispatcher->dispatch( @@ -468,6 +532,8 @@ private function getHeaderPasteNewButton(ModelInterface $parentModel, Environmen ); $parentDefinition = $environment->getParentDataDefinition(); + assert($parentDefinition instanceof ContainerInterface); + /** @var GenerateHtmlEvent $imageEvent */ $imageEvent = $dispatcher->dispatch( new GenerateHtmlEvent( @@ -481,7 +547,7 @@ private function getHeaderPasteNewButton(ModelInterface $parentModel, Environmen '%s', $urlEvent->getUrl(), StringUtil::specialchars($this->translate('pastenew.0', $parentDefinition->getName())), - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -495,14 +561,25 @@ private function getHeaderPasteNewButton(ModelInterface $parentModel, Environmen */ private function getHeaderPasteTopButton(ModelInterface $parentModel, EnvironmentInterface $environment) { - $definition = $environment->getDataDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $dataParentProvider = $basicDefinition->getParentDataProvider(); + assert(\is_string($dataParentProvider)); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); - $filter->andParentIsFromProvider($basicDefinition->getParentDataProvider()); + $filter->andModelIsFromProvider($dataProvider); + $filter->andParentIsFromProvider($dataParentProvider); $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + if ($clipboard->isEmpty($filter)) { return null; } @@ -513,22 +590,33 @@ private function getHeaderPasteTopButton(ModelInterface $parentModel, Environmen $subFilter->andParentIsNot(ModelId::fromModel($parentModel)); $subFilter->orActionIsIn([ItemInterface::COPY, ItemInterface::DEEP_COPY]); + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $dataParentProvider = $basicDefinition->getParentDataProvider(); + assert(is_string($dataParentProvider)); + $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); - $filter->andParentIsFromProvider($basicDefinition->getParentDataProvider()); + $filter->andModelIsFromProvider($dataProvider); + $filter->andParentIsFromProvider($dataParentProvider); $filter->andSub($subFilter); $allowPasteTop = (bool) $clipboard->fetch($filter); } $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + if ($allowPasteTop) { /** @var AddToUrlEvent $urlEvent */ $urlEvent = $dispatcher->dispatch( new AddToUrlEvent( 'act=paste' . '&pid=' . ModelId::fromModel($parentModel)->getSerialized() . - '&after=' . ModelId::fromValues($basicDefinition->getDataProvider(), '0')->getSerialized() + '&after=' . ModelId::fromValues($dataProvider, '0')->getSerialized() ), ContaoEvents::BACKEND_ADD_TO_URL ); @@ -547,7 +635,7 @@ private function getHeaderPasteTopButton(ModelInterface $parentModel, Environmen '%s', $urlEvent->getUrl(), StringUtil::specialchars($this->translate('pasteafter.0', $definition->getName())), - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -565,7 +653,7 @@ private function getHeaderPasteTopButton(ModelInterface $parentModel, Environmen } /** - * Obtain the id of the grand parent (if any). + * Obtain the id of the grand-parent (if any). * * @param ContainerInterface $parentDefinition The parent definition. * @param ModelInterface $parentModel The parent model. @@ -578,11 +666,14 @@ private function getGrandParentId( ModelInterface $parentModel, EnvironmentInterface $environment ) { - if ('' === ($grandParentName = $parentDefinition->getBasicDefinition()->getParentDataProvider())) { + if (null === ($grandParentName = $parentDefinition->getBasicDefinition()->getParentDataProvider())) { return null; } - $relationship = $environment->getDataDefinition()->getModelRelationshipDefinition()->getChildCondition( + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationship = $definition->getModelRelationshipDefinition()->getChildCondition( $grandParentName, $parentDefinition->getName() ); @@ -592,15 +683,21 @@ private function getGrandParentId( } $grandParentProvider = $environment->getDataProvider($grandParentName); + assert($grandParentProvider instanceof DataProviderInterface); $config = $grandParentProvider->getEmptyConfig(); $config->setFilter((array) $relationship->getInverseFilterFor($parentModel)); $parents = $grandParentProvider->fetchAll($config); + assert($parents instanceof CollectionInterface); + + $firstModel = $parents->get(0); + assert($firstModel instanceof ModelInterface); if (1 === $parents->length()) { - return ModelId::fromModel($parents->get(0))->getSerialized(); + return ModelId::fromModel($firstModel)->getSerialized(); } + return false; } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php index a94d2c237..62141f14c 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author David Molineus - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,7 +27,10 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; @@ -74,7 +78,7 @@ public function handleEvent(ActionEvent $event) return; } - if (false !== ($response = $this->process($event->getEnvironment()))) { + if (null !== ($response = $this->process($event->getEnvironment()))) { $event->setResponse($response); } } @@ -88,20 +92,31 @@ public function handleEvent(ActionEvent $event) */ protected function process(EnvironmentInterface $environment) { - $input = $environment->getInputProvider(); - $clipboard = $environment->getClipboard(); - $definition = $environment->getDataDefinition()->getBasicDefinition(); + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); // Tree mode needs special handling. - if ($this->needTreeModeShowAll($definition, $input)) { + if ($this->needTreeModeShowAll($basicDefinition, $input)) { return $this->callAction($environment, 'showAll'); } + $providerName = $basicDefinition->getDataProvider(); + assert(\is_string($providerName)); + // Check if it is a simple create-paste of a single model, if so, redirect to edit view. if ( $this->isSimpleCreatePaste( $clipboard, - $environment->getDataDefinition()->getBasicDefinition()->getDataProvider() + $providerName ) ) { return $this->callAction($environment, 'create'); @@ -113,11 +128,15 @@ protected function process(EnvironmentInterface $environment) $parentModelId = $this->modelIdFromParameter($input, 'pid'); $items = []; - $environment->getController()->applyClipboardActions($source, $after, $into, $parentModelId, null, $items); + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $controller->applyClipboardActions($source, $after, $into, $parentModelId, null, $items); foreach ($items as $item) { $clipboard->remove($item); } + $clipboard->saveTo($environment); // If we use paste all handler don´t redirect yet. @@ -139,7 +158,10 @@ protected function process(EnvironmentInterface $environment) */ private function checkPermission(ActionEvent $event) { - if (true === $event->getEnvironment()->getDataDefinition()->getBasicDefinition()->isEditable()) { + $definition = $event->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (true === $definition->getBasicDefinition()->isEditable()) { return true; } @@ -200,7 +222,7 @@ private function isSimpleCreatePaste(ClipboardInterface $clipboard, $provider) * @param InputProviderInterface $input The input provider. * @param string $name The parameter to retrieve. * - * @return ModelId|null + * @return ModelIdInterface|null */ private function modelIdFromParameter(InputProviderInterface $input, $name) { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php index 27de31fa5..796357791 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php @@ -23,9 +23,11 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ActionHandler; +use ArrayObject; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\System\GetReferrerEvent; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; @@ -33,14 +35,38 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\PrepareMultipleModelsActionEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Command; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandCollectionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PaletteInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +use function array_filter; +use function array_intersect; +use function array_intersect_key; +use function array_map; +use function array_unique; +use function assert; +use function count; +use function in_array; +use function is_array; +use function is_string; +use function method_exists; +use function serialize; +use function sprintf; +use function ucfirst; +use function unserialize; /** * Class SelectController. @@ -84,12 +110,12 @@ public function handleEvent(ActionEvent $event) return; } - if (false !== ($response = $this->process($action, $event->getEnvironment()))) { + if (null !== ($response = $this->process($action, $event->getEnvironment()))) { $event->setResponse($response); // Stop the event here. // Don´t allow any listener for manipulation here. - // Use the sub events their are called. + // Use the sub events there are called. $event->stopPropagation(); } } @@ -100,16 +126,16 @@ public function handleEvent(ActionEvent $event) * @param Action $action The action. * @param EnvironmentInterface $environment The environment. * - * @return string + * @return string|null */ private function process(Action $action, EnvironmentInterface $environment) { - $actionMethod = \sprintf( + $actionMethod = sprintf( 'handle%sAllAction', - \ucfirst($this->getSubmitAction($environment, $this->regardSelectMode($environment))) + ucfirst($this->getSubmitAction($environment, $this->regardSelectMode($environment))) ); - if (false !== ($response = $this->{$actionMethod}($environment, $action))) { + if (null !== ($response = $this->{$actionMethod}($environment, $action))) { return $response; } @@ -126,12 +152,15 @@ private function process(Action $action, EnvironmentInterface $environment) */ private function getSubmitAction(EnvironmentInterface $environment, $regardSelectMode = false) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + if ( !$regardSelectMode - && $environment->getInputProvider()->hasParameter('select') - && !$environment->getInputProvider()->hasValue('properties') + && $inputProvider->hasParameter('select') + && !$inputProvider->hasValue('properties') ) { - return 'select' . \ucfirst($environment->getInputProvider()->getParameter('select')); + return 'select' . ucfirst($inputProvider->getParameter('select')); } if (null !== ($action = $this->determineAction($environment))) { @@ -139,11 +168,11 @@ private function getSubmitAction(EnvironmentInterface $environment, $regardSelec } if ($regardSelectMode) { - return $environment->getInputProvider()->getParameter('mode') ?: null; + return $inputProvider->getParameter('mode') ?: ''; } - return $environment->getInputProvider()->getParameter('select') ? - 'select' . \ucfirst($environment->getInputProvider()->getParameter('select')) : null; + return $inputProvider->getParameter('select') ? + 'select' . ucfirst($inputProvider->getParameter('select')) : ''; } /** @@ -155,13 +184,16 @@ private function getSubmitAction(EnvironmentInterface $environment, $regardSelec */ private function determineAction(EnvironmentInterface $environment) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + foreach (['delete', 'cut', 'copy', 'override', 'edit'] as $action) { if ( - $environment->getInputProvider()->hasValue($action) - || $environment->getInputProvider()->hasValue($action . '_save') - || $environment->getInputProvider()->hasValue($action . '_saveNback') + $inputProvider->hasValue($action) + || $inputProvider->hasValue($action . '_save') + || $inputProvider->hasValue($action . '_saveNback') ) { - $environment->getInputProvider()->setParameter('mode', $action); + $inputProvider->setParameter('mode', $action); return $action; } @@ -180,9 +212,10 @@ private function determineAction(EnvironmentInterface $environment) private function regardSelectMode(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $regardSelectMode = false; - \array_map( + array_map( function ($value) use ($inputProvider, &$regardSelectMode) { if (!$inputProvider->hasValue($value)) { return false; @@ -211,28 +244,31 @@ function ($value) use ($inputProvider, &$regardSelectMode) { * @param Action $action The dcg action. * @param string $submitAction The submit action name. * - * @return ModelId[] + * @return list */ - private function getModelIds(EnvironmentInterface $environment, Action $action, $submitAction) + private function getModelIds(EnvironmentInterface $environment, Action $action, string $submitAction): array { - $valueKey = \in_array($submitAction, ['edit', 'override']) ? 'properties' : 'models'; - $modelIds = (array) $environment->getInputProvider()->getValue($valueKey); + $valueKey = in_array($submitAction, ['edit', 'override']) ? 'properties' : 'models'; - if (!empty($modelIds)) { - $modelIds = \array_map( - function ($modelId) { - return ModelId::fromSerialized($modelId); - }, - $modelIds - ); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); - $event = new PrepareMultipleModelsActionEvent($environment, $action, $modelIds, $submitAction); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $modelIds = array_values((array) $inputProvider->getValue($valueKey)); - $modelIds = $event->getModelIds(); + if (empty($modelIds)) { + return []; } + $modelIds = array_map( + static fn (string $modelId): ModelIdInterface => ModelId::fromSerialized($modelId), + $modelIds + ); + + $event = new PrepareMultipleModelsActionEvent($environment, $action, $modelIds, $submitAction); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $dispatcher->dispatch($event, $event::NAME); - return $modelIds; + return $event->getModelIds(); } /** @@ -251,11 +287,7 @@ private function handleSelectModelsAllAction(EnvironmentInterface $environment, $this->clearClipboard($environment); $this->handleGlobalCommands($environment); - if ($response = $this->callAction($environment, 'selectModelAll')) { - return $response; - } - - return null; + return $this->callAction($environment, 'selectModelAll'); } /** @@ -282,11 +314,7 @@ private function handleSelectPropertiesAllAction(EnvironmentInterface $environme $this->setIntersectProperties($collection, $environment); $this->setIntersectValues($collection, $environment); - if ($response = $this->callAction($environment, 'selectPropertyAll')) { - return $response; - } - - return null; + return $this->callAction($environment, 'selectPropertyAll'); } /** @@ -305,6 +333,8 @@ private function handleDeleteAllAction(EnvironmentInterface $environment, Action $this->handleGlobalCommands($environment); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + foreach ($this->getModelIds($environment, $action, $this->getSubmitAction($environment)) as $modelId) { $inputProvider->setParameter('id', $modelId->getSerialized()); @@ -331,6 +361,8 @@ private function handleDeleteAllAction(EnvironmentInterface $environment, Action private function handleCutAllAction(EnvironmentInterface $environment, Action $action) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + foreach ($this->getModelIds($environment, $action, $this->getSubmitAction($environment)) as $modelId) { $inputProvider->setParameter('source', $modelId->getSerialized()); @@ -357,6 +389,8 @@ private function handleCutAllAction(EnvironmentInterface $environment, Action $a private function handleCopyAllAction(EnvironmentInterface $environment, Action $action) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + foreach ($this->getModelIds($environment, $action, $this->getSubmitAction($environment)) as $modelId) { $inputProvider->setParameter('source', $modelId->getSerialized()); @@ -376,7 +410,7 @@ private function handleCopyAllAction(EnvironmentInterface $environment, Action $ * @param EnvironmentInterface $environment The environment. * @param Action $action The action. * - * @return string + * @return string|null * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.UnusedPrivateMethod) @@ -400,7 +434,7 @@ private function handleEditAllAction(EnvironmentInterface $environment, Action $ * @param EnvironmentInterface $environment The environment. * @param Action $action The action. * - * @return string + * @return string|null * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.UnusedPrivateMethod) @@ -428,11 +462,17 @@ private function handleOverrideAllAction(EnvironmentInterface $environment, Acti private function handleGlobalCommands(EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); - $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($dataDefinition instanceof ContainerInterface); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $globalCommands = $backendView->getGlobalCommands(); + assert($globalCommands instanceof CommandCollectionInterface); $backButton = null; - if ($backendView->getGlobalCommands()->hasCommandNamed('back_button')) { - $backButton = $backendView->getGlobalCommands()->getCommandNamed('back_button'); + if ($globalCommands->hasCommandNamed('back_button')) { + $backButton = $globalCommands->getCommandNamed('back_button'); } if (!$backButton) { @@ -441,7 +481,7 @@ private function handleGlobalCommands(EnvironmentInterface $environment) $parametersBackButton = $backButton->getParameters(); - if (\in_array($this->getSelectAction($environment), ['properties', 'edit'])) { + if (in_array($this->getSelectAction($environment), ['properties', 'edit'])) { $parametersBackButton->offsetSet('act', 'select'); $parametersBackButton->offsetSet( 'select', @@ -464,8 +504,8 @@ private function handleGlobalCommands(EnvironmentInterface $environment) ->setName('close_all_button') ->setLabel('MSC.closeAll.0') ->setDescription('MSC.closeAll.1') - ->setParameters(new \ArrayObject()) - ->setExtra(new \ArrayObject($closeExtra)) + ->setParameters(new ArrayObject()) + ->setExtra(new ArrayObject($closeExtra)) ->setDisabled(false); } @@ -478,7 +518,10 @@ private function handleGlobalCommands(EnvironmentInterface $environment) */ private function getSelectAction(EnvironmentInterface $environment) { - return $environment->getInputProvider()->getParameter('select'); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return $inputProvider->getParameter('select'); } /** @@ -490,14 +533,21 @@ private function getSelectAction(EnvironmentInterface $environment) */ private function getReferrerUrl(EnvironmentInterface $environment) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $parentDefinition = $environment->getParentDataDefinition(); $event = new GetReferrerEvent( true, - (null !== $environment->getParentDataDefinition()) - ? $environment->getParentDataDefinition()->getName() - : $environment->getDataDefinition()->getName() + (null !== $parentDefinition) + ? $parentDefinition->getName() + : $definition->getName() ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::SYSTEM_GET_REFERRER); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::SYSTEM_GET_REFERRER); return $event->getReferrerUrl(); } @@ -511,20 +561,29 @@ private function getReferrerUrl(EnvironmentInterface $environment) */ private function clearClipboard(EnvironmentInterface $environment) { - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); - if ($basicDefinition->getParentDataProvider()) { - $filter->andParentIsFromProvider($basicDefinition->getParentDataProvider()); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(is_string($dataProvider)); + + $filter->andModelIsFromProvider($dataProvider); + if (null !== ($parentProvider = $basicDefinition->getParentDataProvider())) { + $filter->andParentIsFromProvider($parentProvider); } else { $filter->andHasNoParent(); } $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); $items = $clipboard->fetch($filter); - if (\count($items) < 1) { + if (count($items) < 1) { return; } @@ -542,9 +601,15 @@ private function clearClipboard(EnvironmentInterface $environment) */ private function getSelectCollection(EnvironmentInterface $environment) { - $sessionStorage = $environment->getSessionStorage(); $dataDefinition = $environment->getDataDefinition(); - $dataProvider = $environment->getDataProvider(); + assert($dataDefinition instanceof ContainerInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); @@ -553,8 +618,8 @@ private function getSelectCollection(EnvironmentInterface $environment) $modelIds[] = ModelId::fromSerialized($modelId)->getId(); } - $idProperty = \method_exists($dataProvider, 'getIdProperty') ? $dataProvider->getIdProperty() : 'id'; - return $dataProvider->fetchAll( + $idProperty = method_exists($dataProvider, 'getIdProperty') ? $dataProvider->getIdProperty() : 'id'; + $collection = $dataProvider->fetchAll( $dataProvider->getEmptyConfig()->setFilter( [ [ @@ -565,6 +630,8 @@ private function getSelectCollection(EnvironmentInterface $environment) ] ) ); + assert($collection instanceof CollectionInterface); + return $collection; } /** @@ -577,8 +644,11 @@ private function getSelectCollection(EnvironmentInterface $environment) */ private function setIntersectProperties(CollectionInterface $collection, EnvironmentInterface $environment) { - $sessionStorage = $environment->getSessionStorage(); $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); @@ -597,12 +667,15 @@ private function setIntersectProperties(CollectionInterface $collection, Environ */ private function setIntersectValues(CollectionInterface $collection, EnvironmentInterface $environment) { - $sessionStorage = $environment->getSessionStorage(); $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); - if (!$session['intersectProperties'] || !\count($session['intersectProperties'])) { + if (!$session['intersectProperties'] || !count($session['intersectProperties'])) { return; } @@ -620,7 +693,10 @@ private function setIntersectValues(CollectionInterface $collection, Environment */ private function collectIntersectModelProperties(CollectionInterface $collection, EnvironmentInterface $environment) { - $palettesDefinition = $environment->getDataDefinition()->getPalettesDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $palettesDefinition = $definition->getPalettesDefinition(); $properties = []; foreach ($collection->getIterator() as $model) { @@ -636,7 +712,13 @@ private function collectIntersectModelProperties(CollectionInterface $collection } } - return \array_filter( + // We always have to keep the id in the array. + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $idProperty = method_exists($dataProvider, 'getIdProperty') ? $dataProvider->getIdProperty() : 'id'; + $properties[$idProperty] = $collection->count(); + + return array_filter( $properties, function ($count) use ($collection) { return $count === $collection->count(); @@ -654,29 +736,22 @@ function ($count) use ($collection) { */ private function collectIntersectValues(CollectionInterface $collection, EnvironmentInterface $environment) { - $sessionStorage = $environment->getSessionStorage(); $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); $values = []; foreach ($collection->getIterator() as $model) { - $modelValues = \array_intersect_key($model->getPropertiesAsArray(), $session['intersectProperties']); + $modelValues = array_intersect_key($model->getPropertiesAsArray(), $session['intersectProperties']); foreach ($modelValues as $modelProperty => $modelValue) { - if (1 === $collection->count()) { - $values[$modelProperty] = $modelValue; - - continue; - } - $values[$modelProperty][] = $modelValue; } } - if (1 === $collection->count()) { - return $values; - } - $intersectValues = []; foreach ($values as $propertyName => $propertyValues) { if (!($value = $this->getUniqueValueFromArray($propertyValues))) { @@ -699,14 +774,14 @@ private function collectIntersectValues(CollectionInterface $collection, Environ */ private function getVisibleAndEditAbleProperties(PaletteInterface $palette, ModelInterface $model) { - return \array_intersect( - \array_map( + return array_intersect( + array_map( function (PropertyInterface $property) { return $property->getName(); }, $palette->getVisibleProperties($model) ), - \array_map( + array_map( function (PropertyInterface $property) { return $property->getName(); }, @@ -726,63 +801,63 @@ private function getUniqueValueFromArray(array $values) { $serializedValues = false; foreach ($values as $key => $value) { - if (!\is_array($value)) { + if (!is_array($value)) { continue; } - $values[$key] = \serialize($value); + $values[$key] = serialize($value); $serializedValues = true; } if (!$serializedValues) { - return 1 === \count(\array_unique($values)) ? $values[0] : null; + return 1 === count(array_unique($values)) ? $values[0] : null; } - return 1 === \count(\array_unique($values)) ? \unserialize($values[0], ['allowed_classes' => true]) : null; + return 1 === count(array_unique($values)) ? unserialize($values[0], ['allowed_classes' => true]) : null; } /** * Handle session data for override/edit all. * - * @param array $collection The collection. - * @param string $index The session index for the collection. - * @param EnvironmentInterface $environment The environment. + * @param list $collection The collection. + * @param 'models'|'properties' $index The session index for the collection. + * @param EnvironmentInterface $environment The environment. * - * @return array The collection. + * @return list< + * ModelIdInterface> The collection. */ - private function handleSessionOverrideEditAll(array $collection, $index, EnvironmentInterface $environment) + private function handleSessionOverrideEditAll(array $collection, string $index, EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); - $session = []; - if ($sessionStorage->has($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true))) { - $session = - $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); + /** @var array{ + * models: list, + * intersectProperties: array, + * intersectValues: array, + * properties?: list, + * editProperties?: list, + * } $session */ + $session = ['models' => [], 'intersectProperties' => [], 'intersectValues' => []]; + $sessionKey = $dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true); + if ($sessionStorage->has($sessionKey)) { + $session = $sessionStorage->get($sessionKey); } // If collection not empty set to the session and return it. if (!empty($collection)) { - $sessionCollection = \array_map( - function ($item) use ($index) { - if (!\in_array($index, ['models', 'properties'])) { - return $item; - } - - if (!$item instanceof ModelId) { - $item = ModelId::fromSerialized($item); - } - - return $item->getSerialized(); - }, + $sessionCollection = array_map( + static fn (ModelIdInterface $item): string => $item->getSerialized(), $collection ); $session[$index] = $sessionCollection; - $sessionStorage - ->set($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true), $session); + $sessionStorage->set($sessionKey, $session); return $collection; } @@ -793,17 +868,9 @@ function ($item) use ($index) { } // Get the verify collection from the session and return it. - $collection = \array_map( - function ($item) use ($index) { - if (!\in_array($index, ['models', 'properties'])) { - return $item; - } - - return ModelId::fromSerialized($item); - }, - $session[$index] + return array_map( + static fn (string $item): ModelIdInterface => ModelId::fromSerialized($item), + array_values($session[$index]) ); - - return $collection; } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php index 7f6f9ee91..be65435a2 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php @@ -31,16 +31,23 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PropertiesDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\View\ViewInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; use Contao\StringUtil; use LogicException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use function array_merge; use function array_values; @@ -87,22 +94,31 @@ public function handleEvent(ActionEvent $event) */ protected function getModel(EnvironmentInterface $environment) { - $modelId = ModelId::fromSerialized($environment->getInputProvider()->getParameter('id')); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); if ($model) { return $model; } $eventDispatcher = $environment->getEventDispatcher(); + assert($eventDispatcher instanceof EventDispatcherInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); $eventDispatcher->dispatch( new LogEvent( sprintf( 'Could not find ID %s in %s. DC_General show()', $modelId->getId(), - $environment->getDataDefinition()->getName() + $definition->getName() ), __CLASS__ . '::' . __FUNCTION__, 'ERROR' @@ -126,17 +142,20 @@ protected function getModel(EnvironmentInterface $environment) protected function getPropertyLabel(EnvironmentInterface $environment, PropertyInterface $property) { $translator = $environment->getTranslator(); - $key = $property->getLabel(); + assert($translator instanceof TranslatorInterface); + + $key = $property->getLabel(); - if (null === $key) { + if ('' === $key) { throw new LogicException('Missing label for property ' . $property->getName()); } - if ('' !== $key) { - $label = $translator->translate($key, $environment->getDataDefinition()->getName()); - if ($label !== $key) { - return $label; - } + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $label = $translator->translate($key, $definition->getName()); + if ($label !== $key) { + return $label; } $mscKey = 'MSC.' . $property->getName() . '.0'; @@ -161,7 +180,11 @@ protected function getPropertyLabel(EnvironmentInterface $environment, PropertyI protected function convertModel(ModelInterface $model, EnvironmentInterface $environment): array { $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $properties = $definition->getPropertiesDefinition(); + assert($properties instanceof PropertiesDefinitionInterface); + $palette = $definition->getPalettesDefinition()->findPalette($model); $values = [ 'system' => [], @@ -175,9 +198,10 @@ protected function convertModel(ModelInterface $model, EnvironmentInterface $env // Add only visible properties. foreach ($palette->getVisibleProperties($model) as $paletteProperty) { $palettePropertyName = $paletteProperty->getName(); - if (!($visibleProperty = $properties->getProperty($palettePropertyName))) { - throw new DcGeneralRuntimeException('Unable to retrieve property ' . $paletteProperty->getName()); + if (!$properties->hasProperty($palettePropertyName)) { + throw new DcGeneralRuntimeException('Unable to retrieve property ' . $palettePropertyName); } + $visibleProperty = $properties->getProperty($palettePropertyName); // Make it human-readable. $values['visible'][$palettePropertyName] = ViewHelpers::getReadableFieldValue( @@ -196,14 +220,9 @@ protected function convertModel(ModelInterface $model, EnvironmentInterface $env if (isset($values['visible'][$propertyName])) { continue; } - - if (!($systemProperty = $properties->getProperty($propertyName))) { - throw new DcGeneralRuntimeException('Unable to retrieve property ' . $propertyName); - } - $values['system'][$propertyName] = $model->getProperty($propertyName); $labels['system'][$propertyName] = - sprintf('%s [%s]', $this->getPropertyLabel($environment, $systemProperty), $propertyName); + sprintf('%s [%s]', $this->getPropertyLabel($environment, $property), $propertyName); } return [ @@ -249,24 +268,41 @@ protected function getHeadline(TranslatorInterface $translator, $model) */ protected function process(Action $action, EnvironmentInterface $environment) { - if ($environment->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { - return $environment->getView()->edit($action); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $view = $environment->getView(); + assert($view instanceof ViewInterface); + + if ($definition->getBasicDefinition()->isEditOnlyMode()) { + return $view->edit($action); } - $modelId = ModelId::fromSerialized($environment->getInputProvider()->getParameter('id')); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $translator = $environment->getTranslator(); - $model = $this->getModel($environment); - $data = $this->convertModel($model, $environment); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $model = $this->getModel($environment); + assert($model instanceof ModelInterface); + + $data = $this->convertModel($model, $environment); $template = (new ContaoBackendViewTemplate('dcbe_general_show')) ->set('headline', $this->getHeadline($translator, $model)) ->set('arrFields', $data['values']) ->set('arrLabels', $data['labels']); + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + if ($dataProvider instanceof MultiLanguageDataProviderInterface) { $template - ->set('languages', $environment->getController()->getSupportedLanguages($model->getId())) + ->set('languages', $controller->getSupportedLanguages($model->getId())) ->set('currentLanguage', $dataProvider->getCurrentLanguage()) ->set('languageSubmit', $translator->translate('MSC.showSelected')) ->set('backBT', StringUtil::specialchars($translator->translate('MSC.backBT'))); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php index 1dda36f14..6d497bfd7 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author David Molineus * @author Benedict Zinke * @author Ingolf Steinhardt - * @copyright 2013-2021 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,9 +27,13 @@ use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; +use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ToggleCommandInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\TranslatedToggleCommandInterface; @@ -38,6 +42,7 @@ use ContaoCommunityAlliance\DcGeneral\Event\PostPersistModelEvent; use ContaoCommunityAlliance\DcGeneral\Event\PrePersistModelEvent; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class handles toggle commands. @@ -110,7 +115,12 @@ protected function process( ModelIdInterface $modelId = null ) { $dataProvider = $environment->getDataProvider(); - $newState = $this->determineNewState($environment->getInputProvider(), $operation->isInverse()); + assert($dataProvider instanceof DataProviderInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $newState = $this->determineNewState($inputProvider, $operation->isInverse()); // Override the language for language aware toggling. if ( @@ -122,11 +132,21 @@ protected function process( $dataProvider->setCurrentLanguage($operation->getLanguage()); } - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + $provider = $dataProvider->getEmptyConfig(); + assert($provider instanceof ConfigInterface); + assert($modelId instanceof ModelIdInterface); + $config = $provider->setId($modelId->getId()); + assert($config instanceof ConfigInterface); + + $model = $dataProvider->fetch($config); + assert($model instanceof ModelInterface); + $originalModel = clone $model; $originalModel->setId($model->getId()); $model->setProperty($operation->getToggleProperty(), $newState); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $dispatcher->dispatch( new PrePersistModelEvent($environment, $model, $originalModel), @@ -142,6 +162,7 @@ protected function process( // Select the previous language. if (isset($language)) { + /** @var MultiLanguageDataProviderInterface $dataProvider */ $dataProvider->setCurrentLanguage($language); } } @@ -159,16 +180,22 @@ private function checkPermission(ActionEvent $event, ToggleCommandInterface $com { $environment = $event->getEnvironment(); - if (true === $environment->getDataDefinition()->getBasicDefinition()->isEditable() && !$command->isDisabled()) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (true === !$command->isDisabled() && $definition->getBasicDefinition()->isEditable()) { return true; } + $operation = $this->getOperation($event->getAction(), $environment); + assert($operation instanceof ToggleCommandInterface); + $event->setResponse( \sprintf( '
You have no permission for toggle %s.
', - $this->getOperation($event->getAction(), $environment)->getToggleProperty() + $operation->getToggleProperty() ) ); @@ -185,12 +212,16 @@ private function checkPermission(ActionEvent $event, ToggleCommandInterface $com private function getModelId(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if ($inputProvider->hasParameter('id') && $inputProvider->getParameter('id')) { $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); } - if (!(isset($modelId) && ($environment->getDataDefinition()->getName() === $modelId->getDataProviderName()))) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (!(isset($modelId) && ($definition->getName() === $modelId->getDataProviderName()))) { return null; } @@ -203,14 +234,18 @@ private function getModelId(EnvironmentInterface $environment) * @param Action $action The action. * @param EnvironmentInterface $environment The environment. * - * @return CommandInterface + * @return CommandInterface|null */ private function getOperation(Action $action, EnvironmentInterface $environment) { - /** @var Contao2BackendViewDefinitionInterface $definition */ - $definition = $environment->getDataDefinition()->getDefinition(Contao2BackendViewDefinitionInterface::NAME); - $name = $action->getName(); - $commands = $definition->getModelCommands(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $definition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($definition instanceof Contao2BackendViewDefinitionInterface); + + $name = $action->getName(); + $commands = $definition->getModelCommands(); if (!$commands->hasCommandNamed($name)) { return null; diff --git a/src/Contao/View/Contao2BackendView/BaseView.php b/src/Contao/View/Contao2BackendView/BaseView.php index 332d4bb01..800835255 100644 --- a/src/Contao/View/Contao2BackendView/BaseView.php +++ b/src/Contao/View/Contao2BackendView/BaseView.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -18,7 +18,8 @@ * @author David Molineus * @author Martin Treml * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -36,6 +37,8 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGroupHeaderEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetSelectModeButtonsEvent; use ContaoCommunityAlliance\DcGeneral\Controller\Ajax3X; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; @@ -44,13 +47,17 @@ use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * Class BaseView. * - * This class is the base class for the different backend view mode sub classes. + * This class is the base class for the different backend view mode sub-classes. * * @SuppressWarnings(PHPMD.TooManyPublicMethods) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -118,10 +125,22 @@ public function handleAction(ActionEvent $event) { $GLOBALS['TL_CSS']['cca.dc-general.generalDriver'] = 'bundles/ccadcgeneral/css/generalDriver.css'; + $environment = $event->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $environment2 = $this->getEnvironment(); + assert($environment2 instanceof EnvironmentInterface); + + $definition2 = $environment2->getDataDefinition(); + assert($definition2 instanceof ContainerInterface); + if ( (null !== $event->getResponse()) - || $event->getEnvironment()->getDataDefinition()->getName() - !== $this->environment->getDataDefinition()->getName() + || $definition->getName() + !== $definition2->getName() ) { return; } @@ -172,12 +191,15 @@ public function handleAction(ActionEvent $event) */ public function setEnvironment(EnvironmentInterface $environment) { - if ($this->getEnvironment()) { - $this->environment->getEventDispatcher()->removeSubscriber($this); - } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->removeSubscriber($this); $this->environment = $environment; - $this->getEnvironment()->getEventDispatcher()->addSubscriber($this); + $dispatcher->addSubscriber($this); + + return $this; } /** @@ -195,7 +217,13 @@ public function getEnvironment() */ protected function getDataDefinition() { - return $this->getEnvironment()->getDataDefinition(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition; } /** @@ -208,7 +236,13 @@ protected function getDataDefinition() */ protected function translate($path, $section = null) { - return $this->getEnvironment()->getTranslator()->translate($path, $section); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + return $translator->translate($path, $section); } /** @@ -220,7 +254,12 @@ protected function translate($path, $section = null) */ protected function translateFallback($path) { - $translator = $this->getEnvironment()->getTranslator(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + // Try via definition name as domain first. $value = $translator->translate($path, $this->getDataDefinition()->getName()); if ($value !== $path) { @@ -271,7 +310,16 @@ public function getPanel() */ protected function getViewSection() { - return $this->getDataDefinition()->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + return $backendView; } /** @@ -281,7 +329,13 @@ protected function getViewSection() */ protected function isSelectModeActive() { - return 'select' === $this->getEnvironment()->getInputProvider()->getParameter('act'); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return 'select' === $inputProvider->getParameter('act'); } /** @@ -293,20 +347,26 @@ protected function isSelectModeActive() * @param int $groupLength The length of the value to use for grouping (only used when grouping mode is * ListingConfigInterface::GROUP_CHAR). * - * @return string + * @return string|null */ public function formatCurrentValue($field, ModelInterface $model, $groupMode, $groupLength) { $environment = $this->getEnvironment(); - $property = $environment->getDataDefinition()->getPropertiesDefinition()->getProperty($field); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); // No property? Get out! - if (!$property) { + if (!$definition->getPropertiesDefinition()->hasProperty($field)) { return '-'; } $event = new GetGroupHeaderEvent($environment, $model, $field, null, $groupMode, $groupLength); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $dispatcher->dispatch($event, $event::NAME); return $event->getValue(); } @@ -319,10 +379,14 @@ public function formatCurrentValue($field, ModelInterface $model, $groupMode, $g protected function getSelectButtons() { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); $event = new GetSelectModeButtonsEvent($environment); $event->setButtons([]); - $environment->getEventDispatcher()->dispatch($event, GetSelectModeButtonsEvent::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $dispatcher->dispatch($event, GetSelectModeButtonsEvent::NAME); return $event->getButtons(); } @@ -336,7 +400,13 @@ protected function getSelectButtons() */ protected function isMultiLanguage($currentID) { - return (bool) \count($this->getEnvironment()->getController()->getSupportedLanguages($currentID)); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + return (bool) \count($controller->getSupportedLanguages($currentID)); } /** @@ -348,7 +418,13 @@ protected function isMultiLanguage($currentID) */ protected function getTemplate($name) { - return (new ContaoBackendViewTemplate($name))->setTranslator($this->getEnvironment()->getTranslator()); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + return (new ContaoBackendViewTemplate($name))->setTranslator($translator); } /** @@ -361,7 +437,10 @@ protected function getTemplate($name) public function handleAjaxCall() { $environment = $this->getEnvironment(); - $input = $environment->getInputProvider(); + assert($environment instanceof EnvironmentInterface); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); if (true === ($input->hasParameter('id'))) { // Redefine the parameter id if this isn´t model id conform. @@ -371,7 +450,9 @@ public function handleAjaxCall() } $modelId = ModelId::fromSerialized($input->getParameter('id')); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); } $this->addAjaxPropertyForEditAll(); @@ -421,11 +502,18 @@ public function delete(Action $action) */ public function move(Action $action) { - if ($this->getEnvironment()->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->edit($action); } - return \vsprintf($this->notImplMsg, 'move - Mode'); + return \vsprintf($this->notImplMsg, ['move - Mode']); } /** @@ -433,11 +521,17 @@ public function move(Action $action) */ public function undo(Action $action) { - if ($this->getEnvironment()->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->edit($action); } - return \vsprintf($this->notImplMsg, 'undo - Mode'); + return \vsprintf($this->notImplMsg, ['undo - Mode']); } /** @@ -463,13 +557,19 @@ public function edit(Action $action) */ public function showAll(Action $action) { - if ($this->getEnvironment()->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->edit($action); } return \sprintf( $this->notImplMsg, - 'showAll - Mode ' . $this->getEnvironment()->getDataDefinition()->getBasicDefinition()->getMode() + 'showAll - Mode ' . (string) ($definition->getBasicDefinition()->getMode() ?? '') ); } @@ -480,7 +580,10 @@ public function showAll(Action $action) */ protected function generateHeaderButtons() { - return (new GlobalButtonRenderer($this->getEnvironment()))->render(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + return (new GlobalButtonRenderer($environment))->render(); } /** @@ -508,14 +611,18 @@ protected function panel($ignoredPanels = []) public function breadcrumb() { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $event = new GetBreadcrumbEvent($environment); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); $elements = $event->getElements(); - if (!\is_array($elements) || !\count($elements)) { - return null; + if (!\count($elements)) { + return ''; } $GLOBALS['TL_CSS']['cca.dc-general.generalBreadcrumb'] = 'bundles/ccadcgeneral/css/generalBreadcrumb.css'; @@ -532,7 +639,14 @@ public function breadcrumb() */ private function addAjaxPropertyForEditAll() { - $inputProvider = $this->getEnvironment()->getInputProvider(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if ( ('select' !== $inputProvider->getParameter('act')) @@ -547,7 +661,7 @@ private function addAjaxPropertyForEditAll() return; } - $propertiesDefinition = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + $propertiesDefinition = $definition->getPropertiesDefinition(); $propertyClass = \get_class($originalProperty); @@ -568,22 +682,32 @@ private function addAjaxPropertyForEditAll() /** * Find the original property by the modelId. * - * @param string $propertyName The property name. + * @param string|null $propertyName The property name. * * @return PropertyInterface|null */ - private function findOriginalPropertyByModelId($propertyName) + private function findOriginalPropertyByModelId(?string $propertyName): ?PropertyInterface { if (null === $propertyName) { return null; } - $inputProvider = $this->getEnvironment()->getInputProvider(); - $sessionStorage = $this->getEnvironment()->getSessionStorage(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $selectAction = $inputProvider->getParameter('select'); - $session = $sessionStorage->get($this->getEnvironment()->getDataDefinition()->getName() . '.' . $selectAction); + /** @var array{models: list} $session */ + $session = $sessionStorage->get($definition->getName() . '.' . $selectAction); $originalPropertyName = null; foreach ($session['models'] as $modelId) { @@ -599,7 +723,11 @@ private function findOriginalPropertyByModelId($propertyName) $originalPropertyName = \substr($propertyName, \strlen($propertyNamePrefix)); } - $propertiesDefinition = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + if (!$originalPropertyName) { + return null; + } + + $propertiesDefinition = $definition->getPropertiesDefinition(); if (!$propertiesDefinition->hasProperty($originalPropertyName)) { return null; } diff --git a/src/Contao/View/Contao2BackendView/ButtonRenderer.php b/src/Contao/View/Contao2BackendView/ButtonRenderer.php index de0de2057..837855fa9 100644 --- a/src/Contao/View/Contao2BackendView/ButtonRenderer.php +++ b/src/Contao/View/Contao2BackendView/ButtonRenderer.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Ingolf Steinhardt * @author Richard Henkenjohann * @author David Molineus - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,16 +31,19 @@ use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\ItemInterface; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetOperationButtonEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPasteButtonEvent; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandCollectionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CopyCommandInterface; @@ -48,11 +51,13 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ToggleCommandInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\TranslatedToggleCommandInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** - * This class is an helper for rendering the operation buttons in the views. + * This class is a helper for rendering the operation buttons in the views. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) @@ -62,14 +67,14 @@ class ButtonRenderer /** * The ids of all circular contained models. * - * @var string[] + * @var list */ private $circularModelIds; /** * The clipboard items in use. * - * @var ItemInterface[] + * @var list */ private $clipboardItems; @@ -115,23 +120,37 @@ class ButtonRenderer */ public function __construct(EnvironmentInterface $environment) { - $this->environment = $environment; - $this->translator = $environment->getTranslator(); - $this->eventDispatcher = $environment->getEventDispatcher(); - $this->clipboardItems = $this->calculateClipboardItems(); - $dataDefinition = $environment->getDataDefinition(); - $this->commands = $dataDefinition - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME) - ->getModelCommands(); - $controller = $environment->getController(); + $this->environment = $environment; + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $this->translator = $translator; + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $this->eventDispatcher = $dispatcher; + + + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $this->clipboardItems = $this->calculateClipboardItems(); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $this->commands = $backendView->getModelCommands(); + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + $this->clipboardModels = $controller->getModelsFromClipboardItems($this->clipboardItems); $this->circularModelIds = []; // We must only check for CUT operation here as pasting copy'ed parents is allowed. - $cutItems = \array_filter($this->clipboardItems, function ($item) { - /** @var ItemInterface $item */ - return $item->getAction() === $item::CUT; - }); + $cutItems = \array_values(\array_filter( + $this->clipboardItems, + static fn (ItemInterface $item): bool => $item->getAction() === $item::CUT + )); $cutModels = $controller->getModelsFromClipboardItems($cutItems); $collector = new ModelCollector($environment); foreach ($cutModels as $model) { @@ -163,9 +182,9 @@ public function renderButtonsForCollection(CollectionInterface $collection) /** * Render the operation buttons for the passed model. * - * @param ModelInterface $model The model to render the buttons for. - * @param ModelInterface $previous The previous model in the collection. - * @param ModelInterface $next The next model in the collection. + * @param ModelInterface $model The model to render the buttons for. + * @param ModelInterface|null $previous The previous model in the collection. + * @param ModelInterface|null $next The next model in the collection. * * @return void */ @@ -203,14 +222,18 @@ private function renderButtonsFor( $buttonEvent ->setModel($model) ->setCircularReference($isCircular) - ->setPrevious($previous) - ->setNext($next) ->setHrefAfter($urlAfter) ->setHrefInto($urlInto) // Check if the id is in the ignore list. ->setPasteAfterDisabled($isCircular) ->setPasteIntoDisabled($isCircular) ->setContainedModels($this->clipboardModels); + if (null !== $previous) { + $buttonEvent->setPrevious($previous); + } + if (null !== $next) { + $buttonEvent->setNext($next); + } $this->eventDispatcher->dispatch($buttonEvent, GetPasteButtonEvent::NAME); $buttons['pasteafter'] = $this->renderPasteAfterButton($buttonEvent); @@ -233,6 +256,8 @@ private function renderButtonsFor( private function isHierarchical() { $dataDefinition = $this->environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $basicDefinition = $dataDefinition->getBasicDefinition(); return $basicDefinition::MODE_HIERARCHICAL === $basicDefinition->getMode(); @@ -256,8 +281,11 @@ private function hasPasteButtons() */ private function hasPasteNewButton() { - $environment = $this->environment; - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $environment = $this->environment; + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); return ((true === (bool) ViewHelpers::getManualSortingProperty($environment)) && (true === empty($this->clipboardItems)) @@ -268,13 +296,13 @@ private function hasPasteNewButton() /** * Render a command button. * - * @param CommandInterface $command The command to render the button for. - * @param ModelInterface $model The model to which the command shall get applied. - * @param ModelInterface $previous The previous model in the collection. - * @param ModelInterface $next The next model in the collection. - * @param bool $isCircularReference Determinator if there exists a circular reference between the model - * and the model(s) contained in the clipboard. - * @param string[] $childIds The ids of all child models. + * @param CommandInterface $command The command to render the button for. + * @param ModelInterface $model The model to which the command shall get applied. + * @param ModelInterface|null $previous The previous model in the collection. + * @param ModelInterface|null $next The next model in the collection. + * @param bool $isCircularReference Determinator if there exists a circular reference between the + * model and the model(s) contained in the clipboard. + * @param string[] $childIds The ids of all child models. * * @return string */ @@ -309,7 +337,7 @@ private function buildCommand($command, $model, $previous, $next, $isCircularRef ->setObjModel($model) ->setAttributes($attributes) ->setLabel($this->getCommandLabel($command)) - ->setTitle(\sprintf($this->translate((string) $command->getDescription()), $model->getID())) + ->setTitle(\sprintf($this->translate($command->getDescription()), $model->getID())) ->setHref($this->calculateHref($command, $model)) ->setChildRecordIds($childIds) ->setCircularReference($isCircularReference) @@ -331,12 +359,12 @@ private function buildCommand($command, $model, $previous, $next, $isCircularRef if ($icon !== Image::getPath($icon)) { $iconDisabledSuffix = '_'; } - $icon = \substr_replace($icon, $iconDisabledSuffix, \strrpos($icon, '.'), 0); + $icon = \substr_replace($icon, $iconDisabledSuffix, \strrpos($icon, '.') ?: \strlen($icon), 0); } return $this->renderImageAsHtml( $icon, - ($buttonEvent->getLabel() ?? ''), + $buttonEvent->getLabel(), \sprintf( 'title="%s" class="%s"', StringUtil::specialchars($this->translator->translate( @@ -352,10 +380,10 @@ private function buildCommand($command, $model, $previous, $next, $isCircularRef return \sprintf( ' %s', $command->getName(), - $buttonEvent->getHref(), + $buttonEvent->getHref() ?? '', StringUtil::specialchars($buttonEvent->getTitle()), \ltrim($buttonEvent->getAttributes()), - $this->renderImageAsHtml($icon, ($buttonEvent->getLabel() ?? '')) + $this->renderImageAsHtml($icon, $buttonEvent->getLabel()) ); } @@ -408,6 +436,8 @@ private function calculateParameters(CommandInterface $command, $serializedModel $parameters['act'] = $command->getName(); $inputProvider = $this->environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + // If we have a pid add it, used for mode 4 and all parent -> current views. if ($inputProvider->hasParameter('pid')) { $parameters['pid'] = $inputProvider->getParameter('pid'); @@ -462,15 +492,18 @@ private function renderPasteIntoButton(GetPasteButtonEvent $event) return $this->renderImageAsHtml('pasteinto_.svg', $label, 'class="blink"'); } + $model = $event->getModel(); + assert($model instanceof ModelInterface); + if ('pasteinto.1' !== ($opDesc = $this->translate('pasteinto.1'))) { - $title = \sprintf($opDesc, $event->getModel()->getId()); + $title = \sprintf($opDesc, $model->getId()); } else { - $title = \sprintf('%s id %s', $label, $event->getModel()->getId()); + $title = \sprintf('%s id %s', $label, $model->getId()); } return \sprintf( ' %s', - $event->getHrefInto(), + $event->getHrefInto() ?? '', StringUtil::specialchars($title), $this->renderImageAsHtml('pasteinto.svg', $label, 'class="blink"') ); @@ -494,15 +527,18 @@ private function renderPasteAfterButton(GetPasteButtonEvent $event) return $this->renderImageAsHtml('pasteafter_.svg', $label, 'class="blink"'); } + $model = $event->getModel(); + assert($model instanceof ModelInterface); + if ('pasteafter.1' !== ($opDesc = $this->translate('pasteafter.1'))) { - $title = \sprintf($opDesc, $event->getModel()->getId()); + $title = \sprintf($opDesc, $model->getId()); } else { - $title = \sprintf('%s id %s', $label, $event->getModel()->getId()); + $title = \sprintf('%s id %s', $label, $model->getId()); } return \sprintf( ' %s', - $event->getHrefAfter(), + $event->getHrefAfter() ?? '', StringUtil::specialchars($title), $this->renderImageAsHtml('pasteafter.svg', $label, 'class="blink"') ); @@ -511,17 +547,24 @@ private function renderPasteAfterButton(GetPasteButtonEvent $event) /** * Calculate all clipboard items for the current view. * - * @return ItemInterface[] + * @return list */ private function calculateClipboardItems() { $dataDefinition = $this->environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $basicDefinition = $dataDefinition->getBasicDefinition(); $clipboard = $this->environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); - if ($parentProviderName = $basicDefinition->getParentDataProvider()) { + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $filter->andModelIsFromProvider($dataProvider); + if ($parentProviderName = $dataProvider) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); @@ -539,7 +582,10 @@ private function calculateClipboardItems() */ protected function translate($path) { - $value = $this->translator->translate($path, $this->environment->getDataDefinition()->getName()); + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $value = $this->translator->translate($path, $definition->getName()); if ($path !== $value) { return $value; } @@ -564,7 +610,7 @@ private function renderImageAsHtml($src, $alt, $attributes = '') ContaoEvents::IMAGE_GET_HTML ); - return $imageEvent->getHtml(); + return $imageEvent->getHtml() ?? ''; } /** @@ -592,8 +638,9 @@ private function addToUrl($parameters) */ private function isTogglerInActiveState($command, $model) { - $dataProvider = $this->environment->getDataProvider($model->getProviderName()); - $propModel = $model; + $dataProvider = $this->environment->getDataProvider($model->getProviderName()); + $propModel = $model; + $toggleProperty = $command->getToggleProperty(); if ( $command instanceof TranslatedToggleCommandInterface @@ -605,16 +652,19 @@ private function isTogglerInActiveState($command, $model) $dataProvider ->getEmptyConfig() ->setId($model->getId()) - ->setFields([$command->getToggleProperty()]) + ->setFields([$toggleProperty]) ); + if (null === $propModel) { + throw new DcGeneralInvalidArgumentException('Model not found: ' . $model->getId()); + } $dataProvider->setCurrentLanguage($language); } if ($command->isInverse()) { - return !$propModel->getProperty($command->getToggleProperty()); + return !$propModel->getProperty($toggleProperty); } - return (bool) $propModel->getProperty($command->getToggleProperty()); + return (bool) $propModel->getProperty($toggleProperty); } /** @@ -645,15 +695,10 @@ private function calculateHref(CommandInterface $command, $model) */ private function getCommandLabel(CommandInterface $command) { - if (null === $label = $command->getLabel()) { + if ('' === $label = $command->getLabel()) { $label = $command->getName(); } - if (\is_array($label)) { - return $this->translate($label[0]); - } - $label = $this->translate($label); - - return \is_array($label) ? $label[0] : $label; + return $this->translate($label); } } diff --git a/src/Contao/View/Contao2BackendView/ContaoBackendViewTemplate.php b/src/Contao/View/Contao2BackendView/ContaoBackendViewTemplate.php index 6f74ad22d..59fe663da 100644 --- a/src/Contao/View/Contao2BackendView/ContaoBackendViewTemplate.php +++ b/src/Contao/View/Contao2BackendView/ContaoBackendViewTemplate.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author David Molineus * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,21 +28,25 @@ use Contao\System; use ContaoCommunityAlliance\DcGeneral\Clipboard\Clipboard; use ContaoCommunityAlliance\DcGeneral\Contao\InputProvider; +use ContaoCommunityAlliance\DcGeneral\DataDefinitionContainerInterface; use ContaoCommunityAlliance\DcGeneral\DefaultEnvironment; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class is used for the contao backend view as template. + * + * @psalm-suppress PropertyNotSetInConstructor */ class ContaoBackendViewTemplate extends BackendTemplate implements ViewTemplateInterface, TranslatorInterface { /** * The translator. * - * @var TranslatorInterface + * @var TranslatorInterface|null */ - protected $translator; + protected $translator = null; /** * Get the translator. @@ -50,6 +55,9 @@ class ContaoBackendViewTemplate extends BackendTemplate implements ViewTemplateI */ public function getTranslator() { + if (null === $this->translator) { + throw new \RuntimeException('Translator not set.'); + } return $this->translator; } @@ -67,16 +75,6 @@ public function setTranslator(TranslatorInterface $translator) return $this; } - /** - * {@inheritDoc} - */ - public function setData($data) - { - parent::setData($data); - - return $this; - } - /** * {@inheritDoc} */ @@ -100,11 +98,7 @@ public function get($name) */ public function translate($string, $domain = null, array $parameters = [], $locale = null) { - if ($this->translator) { - return $this->translator->translate($string, $domain, $parameters, $locale); - } - - return $string; + return $this->getTranslator()->translate($string, $domain, $parameters, $locale); } /** @@ -112,11 +106,7 @@ public function translate($string, $domain = null, array $parameters = [], $loca */ public function translatePluralized($string, $number, $domain = null, array $parameters = [], $locale = null) { - if ($this->translator) { - return $this->translator->translatePluralized($string, $number, $domain, $parameters, $locale); - } - - return $string; + return $this->getTranslator()->translatePluralized($string, $number, $domain, $parameters, $locale); } /** @@ -124,17 +114,28 @@ public function translatePluralized($string, $number, $domain = null, array $par * * @return string * - * @SuppressWarnings(PHPMD.Superglobals) + * @deprecated Do not use */ public function getBackButton(): string { - $dataContainer = System::getContainer()->get('cca.dc-general.data-definition-container'); - $dataDefinition = $dataContainer->getDefinition($this->table); + $dataContainer = System::getContainer()->get('cca.dc-general.data-definition-container'); + assert($dataContainer instanceof DataDefinitionContainerInterface); + + /** @psalm-suppress UndefinedThisPropertyFetch */ + $table = $this->table; + assert(\is_string($table)); + $dataDefinition = $dataContainer->getDefinition($table); + + $translator = System::getContainer()->get('cca.translator.contao_translator'); + assert($translator instanceof TranslatorInterface); + + $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); $environment = new DefaultEnvironment(); $environment->setDataDefinition($dataDefinition); - $environment->setTranslator(System::getContainer()->get('cca.translator.contao_translator')); - $environment->setEventDispatcher(System::getContainer()->get('event_dispatcher')); + $environment->setTranslator($translator); + $environment->setEventDispatcher($dispatcher); $environment->setInputProvider(new InputProvider()); $environment->setClipboard(new Clipboard()); @@ -150,6 +151,16 @@ public function getData() return parent::getData(); } + /** + * {@inheritDoc} + */ + public function setData($arrData) + { + parent::setData($arrData); + + return $this; + } + /** * {@inheritDoc} */ @@ -163,6 +174,7 @@ public function parse() */ public function output() { + /** @psalm-suppress DeprecatedMethod */ parent::output(); } // @codingStandardsIgnoreEnd diff --git a/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php b/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php index 3f9c39cea..12d3669c2 100644 --- a/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php +++ b/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php @@ -37,13 +37,19 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\DecodePropertyValueForWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ResolveWidgetErrorMessageEvent; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class ContaoWidgetManager. @@ -60,6 +66,8 @@ class ContaoWidgetManager * The environment in use. * * @var ContaoFrameworkInterface + * + * @psalm-suppress DeprecatedInterface */ protected $framework; @@ -87,7 +95,10 @@ public function __construct(EnvironmentInterface $environment, ModelInterface $m { $this->environment = $environment; $this->model = $model; - $this->framework = System::getContainer()->get('contao.framework'); + $framework = System::getContainer()->get('contao.framework'); + assert($framework instanceof ContaoFrameworkInterface); + + $this->framework = $framework; } /** @@ -102,13 +113,15 @@ public function __construct(EnvironmentInterface $environment, ModelInterface $m public function encodeValue($property, $value, PropertyValueBag $propertyValues) { $environment = $this->getEnvironment(); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $event = new EncodePropertyValueFromWidgetEvent($environment, $this->model, $propertyValues); $event ->setProperty($property) ->setValue($value); - $environment->getEventDispatcher()->dispatch($event, EncodePropertyValueFromWidgetEvent::NAME); + $dispatcher->dispatch($event, EncodePropertyValueFromWidgetEvent::NAME); return $event->getValue(); } @@ -124,18 +137,20 @@ public function encodeValue($property, $value, PropertyValueBag $propertyValues) public function decodeValue($property, $value) { $environment = $this->getEnvironment(); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $event = new DecodePropertyValueForWidgetEvent($environment, $this->model); $event ->setProperty($property) ->setValue($value); - $environment->getEventDispatcher()->dispatch($event, DecodePropertyValueForWidgetEvent::NAME); + $dispatcher->dispatch($event, DecodePropertyValueForWidgetEvent::NAME); return $event->getValue(); } /** - * {@inheritdoc} + * @return EnvironmentInterface */ public function getEnvironment() { @@ -143,12 +158,14 @@ public function getEnvironment() } /** - * {@inheritDoc} + * @param string $property + * + * @return bool */ public function hasWidget($property) { try { - return null !== $this->getWidget($property); + return ($this->getWidget($property) instanceof Widget); // @codingStandardsIgnoreStart } catch (\Exception $e) { // Fall though and return false. @@ -167,10 +184,12 @@ public function hasWidget($property) */ public function loadRichTextEditor($buffer, Widget $widget) { + /** @psalm-suppress UndefinedMagicPropertyFetch */ + $rte = $widget->rte; if ( - (null === $widget->rte) - || ((0 !== (\strncmp($widget->rte, 'tiny', 4))) - && (0 !== \strncmp($widget->rte, 'ace', 3))) + (null === $rte) + || ((0 !== (\strncmp($rte, 'tiny', 4))) + && (0 !== \strncmp($rte, 'ace', 3))) ) { return $buffer; } @@ -178,7 +197,7 @@ public function loadRichTextEditor($buffer, Widget $widget) $backendAdapter = $this->framework->getAdapter(Backend::class); $templateLoader = $this->framework->getAdapter(TemplateLoader::class); - [$file, $type] = \explode('|', $widget->rte) + [null, null]; + [$file, $type] = \explode('|', $rte) + ['', '']; $templateName = 'be_' . $file; // This test if the rich text editor template exist. @@ -188,9 +207,9 @@ public function loadRichTextEditor($buffer, Widget $widget) $template ->set('selector', 'ctrl_' . $widget->id) ->set('type', $type) - ->set('readonly', (bool) $widget->readonly); + ->set('readonly', $widget->readonly); - if (0 !== \strncmp($widget->rte, 'tiny', 4)) { + if (0 !== \strncmp($rte, 'tiny', 4)) { /** @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0 */ $template->set('language', $backendAdapter->getTinyMceLanguage()); } @@ -210,7 +229,10 @@ public function loadRichTextEditor($buffer, Widget $widget) protected function getUniqueId($propertyName) { $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $sessionStorage = $this->getEnvironment()->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $selector = 'ctrl_' . $propertyName; @@ -237,21 +259,25 @@ protected function getUniqueId($propertyName) /** * Retrieve the instance of a widget for the given property. * - * @param string $property Name of the property for which the widget shall be retrieved. - * @param PropertyValueBag $inputValues The input values to use (optional). + * @param string $property Name of the property for which the widget shall be retrieved. + * @param PropertyValueBagInterface $inputValues The input values to use (optional). * - * @return Widget + * @return Widget|null * - * @throws DcGeneralRuntimeException When No widget could be build. + * @throws DcGeneralRuntimeException When No widget could be built. * @throws DcGeneralInvalidArgumentException When property is not defined in the property definitions. * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ - public function getWidget($property, PropertyValueBag $inputValues = null) + public function getWidget($property, PropertyValueBagInterface $inputValues = null) { - $environment = $this->getEnvironment(); - $propertyDefinitions = $environment->getDataDefinition()->getPropertiesDefinition(); + $environment = $this->getEnvironment(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + + $propertyDefinitions = $definition->getPropertiesDefinition(); if (!$propertyDefinitions->hasProperty($property)) { throw new DcGeneralInvalidArgumentException( @@ -263,18 +289,19 @@ public function getWidget($property, PropertyValueBag $inputValues = null) $model->setId($this->model->getId()); if ($inputValues) { + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + $values = new PropertyValueBag($inputValues->getArrayCopy()); - $this->environment->getController()->updateModelFromPropertyBag($model, $values); + $controller->updateModelFromPropertyBag($model, $values); } $event = new BuildWidgetEvent($environment, $model, $propertyDefinitions->getProperty($property)); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); - if (!$event->getWidget()) { - throw new DcGeneralRuntimeException( - \sprintf('Widget was not build for property %s::%s.', $this->model->getProviderName(), $property) - ); - } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); return $event->getWidget(); } @@ -292,7 +319,9 @@ public function getWidget($property, PropertyValueBag $inputValues = null) protected function buildDatePicker($objWidget) { $translator = $this->getEnvironment()->getTranslator(); - $strFormat = $GLOBALS['TL_CONFIG'][$objWidget->rgxp . 'Format']; + assert($translator instanceof TranslatorInterface); + + $strFormat = $GLOBALS['TL_CONFIG'][$objWidget->rgxp . 'Format']; switch ($objWidget->rgxp) { case 'datim': @@ -330,14 +359,15 @@ protected function buildDatePicker($objWidget) */ protected function generateHelpText($property) { - $propInfo = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition()->getProperty($property); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $propInfo = $definition->getPropertiesDefinition()->getProperty($property); $label = $propInfo->getDescription(); $widgetType = $propInfo->getWidgetType(); if ( - empty($label) - || ('password' === $widgetType) - || !\is_string($label) + ('password' === $widgetType) || !$GLOBALS['TL_CONFIG']['showHelp'] ) { return ''; @@ -349,31 +379,34 @@ protected function generateHelpText($property) /** * Render the widget for the named property. * - * @param string $property The name of the property for which the widget shall be rendered. - * @param bool $ignoreErrors Flag if the error property of the widget shall get cleared prior rendering. - * @param PropertyValueBag $inputValues The input values to use (optional). + * @param string $property The name of the property for which the widget shall be rendered. + * @param bool $ignoreErrors Flag if the error property of the widget shall get + * cleared prior rendering. + * @param PropertyValueBagInterface $inputValues The input values to use (optional). * * @return string * * @throws DcGeneralRuntimeException For unknown properties. */ - public function renderWidget($property, $ignoreErrors = false, PropertyValueBag $inputValues = null) + public function renderWidget($property, $ignoreErrors = false, PropertyValueBagInterface $inputValues = null) { /** @var Widget $widget */ $widget = $this->getWidget($property, $inputValues); - if (!$widget) { - throw new DcGeneralRuntimeException('No widget for property ' . $property); - } $this->cleanErrors($widget, $ignoreErrors); $this->widgetAddError($property, $widget, $inputValues, $ignoreErrors); - $propInfo = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition()->getProperty($property); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $propInfo = $definition->getPropertiesDefinition()->getProperty($property); + /** @psalm-suppress UndefinedMagicPropertyFetch */ $isHideInput = (bool) $widget->hideInput; $hiddenFields = ($isHideInput) ? $this->buildHiddenFields($widget->value, $widget->name) : null; + /** @psalm-suppress UndefinedMagicPropertyFetch */ $content = (new ContaoBackendViewTemplate('dcbe_general_field')) ->set('strName', $property) ->set('strClass', $widget->tl_class) @@ -406,7 +439,7 @@ public function renderWidget($property, $ignoreErrors = false, PropertyValueBag */ public function buildHiddenFields($value, string $propertyName): array { - if (is_string($value)) { + if (\is_string($value)) { return [$propertyName => $value]; } @@ -415,7 +448,7 @@ public function buildHiddenFields($value, string $propertyName): array $values[] = $this->buildHiddenFields($item, $propertyName . '[' . $key . ']'); } - return array_merge(...$values); + return \array_merge(...$values); } /** @@ -424,7 +457,7 @@ public function buildHiddenFields($value, string $propertyName): array * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ - public function processInput(PropertyValueBag $propertyValues) + public function processInput(PropertyValueBag $propertyValues): void { // @codingStandardsIgnoreStart - Remember current POST data and clear it. $post = $_POST; @@ -441,8 +474,10 @@ public function processInput(PropertyValueBag $propertyValues) foreach (\array_keys($propertyValues->getArrayCopy()) as $property) { // NOTE: the passed input values are RAW DATA from the input provider - aka widget known values and not // native data as in the model. - // Therefore we do not need to decode them but MUST encode them. + // Therefore, we do not need to decode them but MUST encode them. $widget = $this->getWidget($property, $propertyValues); + assert($widget instanceof Widget); + $widget->validate(); if ($widget->hasErrors()) { @@ -471,7 +506,7 @@ public function processInput(PropertyValueBag $propertyValues) /** * {@inheritDoc} */ - public function processErrors(PropertyValueBag $propertyValues) + public function processErrors(PropertyValueBag $propertyValues): void { $propertyErrors = $propertyValues->getInvalidPropertyErrors(); @@ -480,9 +515,11 @@ public function processErrors(PropertyValueBag $propertyValues) } $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); foreach ($propertyErrors as $property => $errors) { $widget = $this->getWidget($property); + assert($widget instanceof Widget); foreach ($errors as $error) { $event = new ResolveWidgetErrorMessageEvent($this->getEnvironment(), $error); @@ -499,6 +536,8 @@ public function processErrors(PropertyValueBag $propertyValues) * @param bool $ignoreErrors The flag for errors cleared. * * @return void + * + * @throws \ReflectionException */ protected function cleanErrors(Widget $widget, $ignoreErrors = false) { diff --git a/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php b/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php index f93d1ddd8..b393ee960 100644 --- a/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php +++ b/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author David Molineus * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +27,7 @@ use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\RedirectEvent; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\Item; use ContaoCommunityAlliance\DcGeneral\Clipboard\ItemInterface; @@ -33,13 +35,18 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; use ContaoCommunityAlliance\DcGeneral\DcGeneralViews; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Event\FormatModelLabelEvent; use ContaoCommunityAlliance\DcGeneral\Event\ViewEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -123,8 +130,15 @@ private function checkPermission(ActionEvent $event) { $actionName = $event->getAction()->getName(); - $environment = $event->getEnvironment(); - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $environment = $event->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $basicDefinition = $definition->getBasicDefinition(); if ( (('create' === $actionName) && (true === $basicDefinition->isCreatable())) @@ -137,11 +151,11 @@ private function checkPermission(ActionEvent $event) $permissionMessage = 'You have no permission for model ' . $actionName . ' '; switch ($actionName) { case 'create': - $permissionMessage .= 'in ' . $environment->getDataDefinition()->getName(); + $permissionMessage .= 'in ' . $definition->getName(); break; case 'cut': - $permissionMessage .= $environment->getInputProvider()->getParameter('source'); + $permissionMessage .= $inputProvider->getParameter('source'); break; default: @@ -167,10 +181,16 @@ private function checkPermission(ActionEvent $event) */ private function clearClipboard(ActionEvent $event, $redirect = true) { - $environment = $event->getEnvironment(); + $environment = $event->getEnvironment(); + $eventDispatcher = $environment->getEventDispatcher(); - $clipboard = $environment->getClipboard(); - $input = $environment->getInputProvider(); + assert($eventDispatcher instanceof EventDispatcherInterface); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); if ($clipboardId = $input->getParameter('clipboard-item')) { $clipboard->removeByClipboardId($clipboardId); @@ -224,8 +244,13 @@ private function addToClipboard(ActionEvent $event) { $actionName = $event->getAction()->getName(); $environment = $event->getEnvironment(); - $input = $environment->getInputProvider(); - $clipboard = $environment->getClipboard(); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + $parentIdRaw = $input->getParameter('pid'); if ($parentIdRaw) { @@ -243,7 +268,12 @@ private function addToClipboard(ActionEvent $event) return; } - $providerName = $environment->getDataDefinition()->getBasicDefinition()->getDataProvider(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $providerName = $definition->getBasicDefinition()->getDataProvider(); + assert(\is_string($providerName)); + $item = new UnsavedItem($clipboardActionName, $parentId, $providerName); // Remove other create items, there can only be one create item in the clipboard or many others @@ -287,6 +317,7 @@ private function addToClipboard(ActionEvent $event) protected function isAddingAllowed(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); // No manual sorting property defined, no need to add it to the clipboard. // Or we already have an after or into attribute, a handler can pick it up. @@ -313,23 +344,38 @@ public function handleView(ViewEvent $event) return; } - $environment = $event->getEnvironment(); - $input = $environment->getInputProvider(); + $environment = $event->getEnvironment(); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + $eventDispatcher = $environment->getEventDispatcher(); - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + assert($eventDispatcher instanceof EventDispatcherInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + $filter->andModelIsFromProvider($dataProvider); if ($parentProviderName = $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + $options = []; - foreach ($environment->getClipboard()->fetch($filter) as $item) { + foreach ($clipboard->fetch($filter) as $item) { $modelId = $item->getModelId(); $dataProvider = $environment->getDataProvider($item->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); if ($modelId) { $config = $dataProvider->getEmptyConfig(); @@ -348,7 +394,11 @@ public function handleView(ViewEvent $event) $label = $label['content']; } else { $model = $dataProvider->getEmptyModel(); - $label = $environment->getTranslator()->translate('new.0', $item->getDataProviderName()); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $label = $translator->translate('new.0', $item->getDataProviderName()); } $options[$item->getClipboardId()] = ['item' => $item, 'model' => $model, 'label' => $label]; @@ -385,13 +435,18 @@ public function handleView(ViewEvent $event) */ private function removeItemsFromClipboard(ActionEvent $event) { - $environment = $event->getEnvironment(); - if ('select' === $environment->getInputProvider()->getParameter('act')) { + $environment = $event->getEnvironment(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if ('select' === $inputProvider->getParameter('act')) { return; } $clipboard = $environment->getClipboard(); - $filter = new Filter(); + assert($clipboard instanceof ClipboardInterface); + + $filter = new Filter(); $filter->andActionIs(ItemInterface::CREATE); $items = $clipboard->fetch($filter); foreach ($items as $item) { diff --git a/src/Contao/View/Contao2BackendView/EditMask.php b/src/Contao/View/Contao2BackendView/EditMask.php index 2a4704f8e..64cadd2ee 100644 --- a/src/Contao/View/Contao2BackendView/EditMask.php +++ b/src/Contao/View/Contao2BackendView/EditMask.php @@ -26,6 +26,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView; use Contao\BackendUser; +use Contao\CoreBundle\Intl\Locales; use Contao\Image; use Contao\System; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; @@ -33,13 +34,18 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\RedirectEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\System\GetReferrerEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\System\LogEvent; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetEditMaskSubHeadlineEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetEditModeButtonsEvent; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\DefaultEditInformation; +use ContaoCommunityAlliance\DcGeneral\Data\EditInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PropertiesDefinitionInterface; @@ -53,6 +59,10 @@ use ContaoCommunityAlliance\DcGeneral\Event\PrePersistModelEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class manages the displaying of the edit/create mask containing the widgets. @@ -61,6 +71,7 @@ * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassLength) */ class EditMask { @@ -109,20 +120,20 @@ class EditMask /** * The default edit information. * - * @var DefaultEditInformation + * @var EditInformationInterface */ - private DefaultEditInformation $editInformation; + private EditInformationInterface $editInformation; /** * Create the edit mask. * - * @param BackendViewInterface $view The view in use. - * @param ModelInterface $model The model with the current data. - * @param ModelInterface $originalModel The data from the original data. - * @param callable $preFunction The function to call before saving an item. - * @param callable $postFunction The function to call after saving an item. - * @param string $breadcrumb The rendered breadcrumb. - * @param DefaultEditInformation $editInformation The default edit information. + * @param BackendViewInterface $view The view in use. + * @param ModelInterface $model The model with the current data. + * @param ModelInterface $originalModel The data from the original data. + * @param callable|null $preFunction The function to call before saving an item. + * @param callable|null $postFunction The function to call after saving an item. + * @param string $breadcrumb The rendered breadcrumb. + * @param EditInformationInterface|null $editInformation The default edit information. */ public function __construct( $view, @@ -131,9 +142,12 @@ public function __construct( $preFunction, $postFunction, $breadcrumb, - ?DefaultEditInformation $editInformation = null + ?EditInformationInterface $editInformation = null ) { - $this->environment = $view->getEnvironment(); + if (null === $environment = $view->getEnvironment()) { + throw new \InvalidArgumentException('View has no environment'); + } + $this->environment = $environment; $this->model = $model; $this->originalModel = $originalModel; $this->preFunction = $preFunction; @@ -148,6 +162,7 @@ public function __construct( ); // @codingStandardsIgnoreEnd $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); } $this->editInformation = $editInformation; @@ -170,7 +185,10 @@ public function getEnvironment() */ protected function getDataDefinition() { - return $this->getEnvironment()->getDataDefinition(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition; } /** @@ -180,7 +198,10 @@ protected function getDataDefinition() */ protected function isPopup() { - return $this->getEnvironment()->getInputProvider()->getParameter('popup'); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return $inputProvider->getParameter('popup'); } /** @@ -200,7 +221,11 @@ protected function checkEditable($model) // Check if table is editable. if ($model->getId() && !$definition->getBasicDefinition()->isEditable()) { $message = 'DataContainer ' . $definition->getName() . ' is not editable'; - $environment->getEventDispatcher()->dispatch( + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch( new LogEvent($message, 'ERROR', 'DC_General - edit()'), ContaoEvents::SYSTEM_LOG ); @@ -225,7 +250,11 @@ protected function checkCreatable($model) // Check if table is closed, but we are adding a new item. if (!($model->getId() || $definition->getBasicDefinition()->isCreatable())) { $message = 'DataContainer ' . $definition->getName() . ' is closed'; - $environment->getEventDispatcher()->dispatch( + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch( new LogEvent($message, 'ERROR', 'DC_General - edit()'), ContaoEvents::SYSTEM_LOG ); @@ -265,6 +294,7 @@ protected function ensurePropertyExists($property, $propertyDefinitions) protected function processInput($widgetManager) { $input = $this->getEnvironment()->getInputProvider(); + assert($input instanceof InputProviderInterface); if ($input->getValue('FORM_SUBMIT') === $this->getDataDefinition()->getName()) { $propertyValues = new PropertyValueBag(); @@ -295,11 +325,15 @@ protected function processInput($widgetManager) protected function handlePrePersist() { $environment = $this->getEnvironment(); + if (null !== $this->preFunction) { \call_user_func($this->preFunction, $environment, $this->model, $this->originalModel); } - $environment->getEventDispatcher()->dispatch( + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch( new PrePersistModelEvent($environment, $this->model, $this->originalModel), PrePersistModelEvent::NAME, ); @@ -313,12 +347,16 @@ protected function handlePrePersist() protected function handlePostPersist() { $environment = $this->getEnvironment(); + if (null !== $this->postFunction) { \call_user_func($this->postFunction, $environment, $this->model, $this->originalModel); } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $event = new PostPersistModelEvent($environment, $this->model, $this->originalModel); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); } /** @@ -336,6 +374,8 @@ protected function handlePostPersist() protected function getButtonLabel($buttonLabel) { $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + if (($label = $translator->translate($buttonLabel, $this->getDataDefinition()->getName())) !== $buttonLabel) { return $label; } @@ -358,6 +398,9 @@ protected function getButtonLabel($buttonLabel) */ protected function getEditButtons() { + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $buttons = []; $basicDefinition = $this->getDataDefinition()->getBasicDefinition(); @@ -377,7 +420,7 @@ protected function getEditButtons() ); $buttons['save'] = $buttonTemplate->parse(); - if (!$this->getEnvironment()->getInputProvider()->getParameter('nb')) { + if (!$inputProvider->getParameter('nb')) { $buttonTemplate->setData( [ 'label' => $this->getButtonLabel('saveNclose'), @@ -395,7 +438,7 @@ protected function getEditButtons() if ( $basicDefinition->isCreatable() - && !$this->getEnvironment()->getInputProvider()->getParameter('nc') + && !$inputProvider->getParameter('nc') ) { $buttonTemplate->setData( [ @@ -412,7 +455,7 @@ protected function getEditButtons() $buttons['saveNcreate'] = $buttonTemplate->parse(); } - if ($this->getEnvironment()->getInputProvider()->hasParameter('s2e')) { + if ($inputProvider->hasParameter('s2e')) { $buttonTemplate->setData( [ 'label' => $this->getButtonLabel('saveNedit'), @@ -452,7 +495,10 @@ protected function getEditButtons() $event = new GetEditModeButtonsEvent($this->getEnvironment()); $event->setButtons($buttons); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); $submitButtons = ['toggleIcon' => Image::getHtml('navcol.svg')]; $editButtons = $event->getButtons(); @@ -474,9 +520,9 @@ protected function getEditButtons() /** * Build the field sets. * - * @param ContaoWidgetManager $widgetManager The widget manager in use. - * @param PaletteInterface $palette The palette to use. - * @param PropertyValueBag $propertyValues The property values. + * @param ContaoWidgetManager $widgetManager The widget manager in use. + * @param PaletteInterface $palette The palette to use. + * @param PropertyValueBag|null $propertyValues The property values. * * @return array * @@ -485,13 +531,21 @@ protected function getEditButtons() */ protected function buildFieldSet($widgetManager, $palette, $propertyValues) { - $environment = $this->getEnvironment(); - $definition = $this->getDataDefinition(); - $translator = $environment->getTranslator(); + $environment = $this->getEnvironment(); + $definition = $this->getDataDefinition(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $propertyDefinitions = $definition->getPropertiesDefinition(); - $isAutoSubmit = ('auto' === $environment->getInputProvider()->getValue('SUBMIT_TYPE')); + $isAutoSubmit = ('auto' === $inputProvider->getValue('SUBMIT_TYPE')); $legendStates = $this->getLegendStates(); - $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); $fieldSets = []; $first = true; @@ -501,6 +555,7 @@ protected function buildFieldSet($widgetManager, $palette, $propertyValues) $definition->getName() ); $fields = []; + $fieldSet = []; $properties = $legend->getProperties($this->model, $propertyValues); if (!$properties) { @@ -534,7 +589,7 @@ protected function buildFieldSet($widgetManager, $palette, $propertyValues) if (!$property->isEditable($this->model, $propertyValues, $legend)) { $propertyDefinition = $propertyDefinitions->getProperty($property->getName()); $propertyDefinition->setExtra( - array_merge(($propertyDefinition->getExtra() ?? []), ['readonly' => true]) + \array_merge(($propertyDefinition->getExtra()), ['readonly' => true]) ); } @@ -571,10 +626,13 @@ protected function buildFieldSet($widgetManager, $palette, $propertyValues) */ protected function storeVersion(ModelInterface $model) { - $modelId = $model->getId(); - $environment = $this->getEnvironment(); - $definition = $this->getDataDefinition(); - $dataProvider = $environment->getDataProvider($model->getProviderName()); + $modelId = $model->getId(); + $environment = $this->getEnvironment(); + $definition = $this->getDataDefinition(); + + $dataProvider = $environment->getDataProvider($model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $dataProviderDefinition = $definition->getDataProviderDefinition(); $dataProviderInformation = $dataProviderDefinition->getInformation($model->getProviderName()); @@ -584,13 +642,18 @@ protected function storeVersion(ModelInterface $model) // Compare version and current record. $currentVersion = $dataProvider->getActiveVersion($modelId); + $model = $dataProvider->getVersion($modelId, $currentVersion); + assert($model instanceof ModelInterface); if ( !$currentVersion - || !$dataProvider->sameModels($model, $dataProvider->getVersion($modelId, $currentVersion)) + || !$dataProvider->sameModels($model, $model) ) { $user = BackendUser::getInstance(); - $dataProvider->saveVersion($model, $user->username); + $username = $user->username; + assert(\is_string($username)); + + $dataProvider->saveVersion($model, $username); } } @@ -618,9 +681,13 @@ protected function getManualSortingProperty() */ protected function handleSubmit(ModelInterface $model) { - $environment = $this->getEnvironment(); - $dispatcher = $environment->getEventDispatcher(); + $environment = $this->getEnvironment(); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if ($inputProvider->hasValue('save')) { $newUrlEvent = new AddToUrlEvent('act=edit&btn=s&id=' . ModelId::fromModel($model)->getSerialized()); @@ -645,7 +712,10 @@ protected function handleSubmit(ModelInterface $model) } elseif ($inputProvider->hasValue('saveNback')) { $this->clearBackendStates(); - $parentProviderName = $environment->getDataDefinition()->getBasicDefinition()->getParentDataProvider(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $parentProviderName = $definition->getBasicDefinition()->getParentDataProvider(); $newUrlEvent = new GetReferrerEvent(false, $parentProviderName); $dispatcher->dispatch($newUrlEvent, ContaoEvents::SYSTEM_GET_REFERRER); @@ -656,7 +726,7 @@ protected function handleSubmit(ModelInterface $model) /** * Determine the headline to use. * - * @return string. + * @return string * * @deprecated This is deprecated since 2.3 and will be removed in 3.0. */ @@ -672,7 +742,7 @@ protected function getHeadline(): string /** * Determine the headline to use. * - * @return string. + * @return string */ protected function getSubHeadline(): string { @@ -680,9 +750,12 @@ protected function getSubHeadline(): string $event = new GetEditMaskSubHeadlineEvent($this->environment, $this->model); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); - return $event->getHeadline(); + return (string) $event->getHeadline(); } /** @@ -692,12 +765,17 @@ protected function getSubHeadline(): string * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.LongVariable) */ protected function doPersist() { - $environment = $this->getEnvironment(); - $dataProvider = $environment->getDataProvider($this->model->getProviderName()); + $environment = $this->getEnvironment(); + + $dataProvider = $environment->getDataProvider($this->model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if (!$this->model->getMeta(ModelInterface::IS_CHANGED)) { return true; @@ -714,38 +792,49 @@ protected function doPersist() $models->push($this->model); $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $manualSortingProperty = $this->getManualSortingProperty(); + assert(\is_string($manualSortingProperty)); if ($inputProvider->hasParameter('after')) { $after = ModelId::fromSerialized($inputProvider->getParameter('after')); $previousDataProvider = $environment->getDataProvider($after->getDataProviderName()); + assert($previousDataProvider instanceof DataProviderInterface); + $previousFetchConfig = $previousDataProvider->getEmptyConfig(); $previousFetchConfig->setId($after->getId()); $previousModel = $previousDataProvider->fetch($previousFetchConfig); if ($previousModel) { - $controller->pasteAfter($previousModel, $models, $this->getManualSortingProperty()); + $controller->pasteAfter($previousModel, $models, $manualSortingProperty); } else { - $controller->pasteTop($models, $this->getManualSortingProperty()); + $controller->pasteTop($models, $manualSortingProperty); } } elseif ($inputProvider->hasParameter('into')) { $into = ModelId::fromSerialized($inputProvider->getParameter('into')); $parentDataProvider = $environment->getDataProvider($into->getDataProviderName()); + assert($parentDataProvider instanceof DataProviderInterface); + $parentFetchConfig = $parentDataProvider->getEmptyConfig(); $parentFetchConfig->setId($into->getId()); $parentModel = $parentDataProvider->fetch($parentFetchConfig); if ($parentModel) { - $controller->pasteInto($parentModel, $models, $this->getManualSortingProperty()); + $controller->pasteInto($parentModel, $models, $manualSortingProperty); } else { - $controller->pasteTop($models, $this->getManualSortingProperty()); + $controller->pasteTop($models, $manualSortingProperty); } } else { - $controller->pasteTop($models, $this->getManualSortingProperty()); + $controller->pasteTop($models, $manualSortingProperty); } - $environment->getClipboard()->clear()->saveTo($environment); + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + $clipboard->clear()->saveTo($environment); } else { if (!$this->allValuesUnique()) { return false; @@ -772,10 +861,16 @@ protected function doPersist() protected function allValuesUnique() { // Init some vars. - $environment = $this->getEnvironment(); - $translator = $environment->getTranslator(); - $dataProvider = $environment->getDataProvider($this->model->getProviderName()); + $environment = $this->getEnvironment(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $dataProvider = $environment->getDataProvider($this->model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); // Run each and check the unique flag. foreach ($this->getDataDefinition()->getPropertiesDefinition()->getPropertyNames() as $propertyName) { @@ -812,77 +907,102 @@ protected function allValuesUnique() * * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.Superglobals) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute() { - $environment = $this->getEnvironment(); - $definition = $this->getDataDefinition(); - $dataProvider = $environment->getDataProvider($this->model->getProviderName()); + $environment = $this->getEnvironment(); + $definition = $this->getDataDefinition(); + + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $model = $this->model; + assert($model instanceof ModelInterface); + + $dataProvider = $environment->getDataProvider($model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $dataProviderDefinition = $definition->getDataProviderDefinition(); - $dataProviderInformation = $dataProviderDefinition->getInformation($this->model->getProviderName()); - $inputProvider = $environment->getInputProvider(); - $palettesDefinition = $definition->getPalettesDefinition(); - $submitted = ($definition->getName() === $inputProvider->getValue('FORM_SUBMIT')); - $isAutoSubmit = ('auto' === $inputProvider->getValue('SUBMIT_TYPE')); - $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + $dataProviderInformation = $dataProviderDefinition->getInformation($model->getProviderName()); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $palettesDefinition = $definition->getPalettesDefinition(); + $submitted = ($definition->getName() === $inputProvider->getValue('FORM_SUBMIT')); + $isAutoSubmit = ('auto' === $inputProvider->getValue('SUBMIT_TYPE')); - $widgetManager = new ContaoWidgetManager($environment, $this->model); + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); - $this->checkEditable($this->model); - $this->checkCreatable($this->model); + $widgetManager = new ContaoWidgetManager($environment, $model); - $environment->getEventDispatcher()->dispatch( - new PreEditModelEvent($environment, $this->model), + $this->checkEditable($model); + $this->checkCreatable($model); + + $dispatcher->dispatch( + new PreEditModelEvent($environment, $model), PreEditModelEvent::NAME ); - $environment->getEventDispatcher()->dispatch( - new EnforceModelRelationshipEvent($this->getEnvironment(), $this->model), + $dispatcher->dispatch( + new EnforceModelRelationshipEvent($this->getEnvironment(), $model), DcGeneralEvents::ENFORCE_MODEL_RELATIONSHIP ); // Pass 1: Get the palette for the values stored in the model. - $palette = $palettesDefinition->findPalette($this->model); + $palette = $palettesDefinition->findPalette($model); // Check if input mask has visible properties. - if (!\count($palette->getProperties($this->model))) { + if (!\count($palette->getProperties($model))) { // @codingStandardsIgnoreStart \trigger_error('No visible properties for this edit mask defined!', E_USER_ERROR); // @codingStandardsIgnoreEnd } $propertyValues = $this->processInput($widgetManager); + if ($submitted && $propertyValues) { // Pass 2: Determine the real palette we want to work on if we have some data submitted. - $palette = $palettesDefinition->findPalette($this->model, $propertyValues); + $palette = $palettesDefinition->findPalette($model, $propertyValues); // Update the model - the model might add some more errors to the propertyValueBag via exceptions. - $this->getEnvironment()->getController()->updateModelFromPropertyBag($this->model, $propertyValues); + $controller->updateModelFromPropertyBag($model, $propertyValues); } $fieldSets = $this->buildFieldSet($widgetManager, $palette, $propertyValues); - if ((!$isAutoSubmit) && $submitted && !$editInformation->getModelError($this->model)) { + if ((!$isAutoSubmit) && $submitted && !$editInformation->getModelError($model)) { if ($this->doPersist()) { - $this->handleSubmit($this->model); + $this->handleSubmit($model); } } + $errors = null; + if ($editInformation instanceof DefaultEditInformation) { + $errors = $editInformation->getFlatModelErrors($model); + } + $viewTemplate = new ContaoBackendViewTemplate('dcbe_general_edit'); $viewTemplate->setData( [ 'fieldsets' => $fieldSets, 'versions' => $dataProviderInformation->isVersioningEnabled() ? $dataProvider->getVersions( - $this->model->getId() + $model->getId() ) : null, 'subHeadline' => $this->getSubHeadline(), 'table' => $definition->getName(), 'enctype' => 'multipart/form-data', - 'error' => $editInformation->getFlatModelErrors($this->model), + 'error' => $errors, 'editButtons' => $this->getEditButtons(), 'noReload' => $editInformation->hasAnyModelError(), 'breadcrumb' => $this->breadcrumb, - 'model' => $this->model + 'model' => $model ] ); @@ -900,26 +1020,35 @@ public function execute() */ private function executeMultiLanguage(ContaoBackendViewTemplate $template) { + $dataProvider = $this->getEnvironment()->getDataProvider($this->model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + if ( \in_array( MultiLanguageDataProviderInterface::class, - \class_implements( - $this->getEnvironment()->getDataProvider( - $this->model->getProviderName() - ) - ) + \class_implements($dataProvider) ) ) { /** @var MultiLanguageDataProviderInterface $dataProvider */ $dataProvider = $this->getEnvironment()->getDataProvider(); - $languages = System::getLanguages(); + + $locales = System::getContainer()->get('contao.intl.locales'); + assert($locales instanceof Locales); + + $languages = $locales->getLocales(null, true); + + $controller = $this->environment->getController(); + assert($controller instanceof ControllerInterface); + + $translator = $this->environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $template->set( 'languages', - $this->getEnvironment()->getController()->getSupportedLanguages($this->model->getId()) + $controller->getSupportedLanguages($this->model->getId()) ) ->set('language', $dataProvider->getCurrentLanguage()) - ->set('languageSubmit', $this->environment->getTranslator()->translate('MSC.showSelected')) + ->set('languageSubmit', $translator->translate('MSC.showSelected')) ->set('languageHeadline', $languages[$dataProvider->getCurrentLanguage()] ?? ''); return; @@ -940,7 +1069,7 @@ private function executeMultiLanguage(ContaoBackendViewTemplate $template) */ protected function clearBackendStates() { - \setcookie('BE_PAGE_OFFSET', 0, 0, '/'); + \setcookie('BE_PAGE_OFFSET', '', 0, '/'); $_SESSION['TL_INFO'] = []; $_SESSION['TL_ERROR'] = []; @@ -971,19 +1100,21 @@ private function isLegendVisible($legend, $legendStates) */ private function getLegendStates() { - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); - $legendStates = $environment->getSessionStorage()->get('LEGENDS') ?: []; + $environment = $this->getEnvironment(); - if (\array_key_exists($definition->getName(), $legendStates)) { - $legendStates = $legendStates[$definition->getName()]; + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); - return $legendStates; - } + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + $legendStates = $sessionStorage->get('LEGENDS') ?: []; - $legendStates = []; + if (\array_key_exists($definition->getName(), $legendStates)) { + return $legendStates[$definition->getName()]; + } - return $legendStates; + return []; } /** diff --git a/src/Contao/View/Contao2BackendView/Event/BaseButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/BaseButtonEvent.php index b96695eab..50962ccfc 100644 --- a/src/Contao/View/Contao2BackendView/Event/BaseButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/BaseButtonEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,6 +33,8 @@ class BaseButtonEvent extends AbstractEnvironmentAwareEvent { /** * The name of the event. + * + * @var string */ public const NAME = 'dc-general.view.contao2backend.button'; @@ -40,35 +43,35 @@ class BaseButtonEvent extends AbstractEnvironmentAwareEvent * * @var string */ - protected $attributes; + protected $attributes = ''; /** * The Html code to use for this button. * - * @var string + * @var string|null */ - protected $html; + protected $html = null; /** * The key/name of the button. * * @var string */ - protected $key; + protected $key = ''; /** * The label to use for the button. * * @var string */ - protected $label; + protected $label = ''; /** * The title to use for the button. * * @var string */ - protected $title; + protected $title = ''; /** * Set the HTML attributes for the button. @@ -113,7 +116,7 @@ public function setHtml($html) /** * Get the HTML code for the button. * - * @return string + * @return string|null */ public function getHtml() { diff --git a/src/Contao/View/Contao2BackendView/Event/BaseGetButtonsEvent.php b/src/Contao/View/Contao2BackendView/Event/BaseGetButtonsEvent.php index 42f54c8b1..7c4d7cca4 100644 --- a/src/Contao/View/Contao2BackendView/Event/BaseGetButtonsEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/BaseGetButtonsEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,6 +34,8 @@ class BaseGetButtonsEvent extends AbstractEnvironmentAwareEvent { /** * The name of the event. + * + * @var string */ public const NAME = 'dc-general.view.contao2backend.get-buttons'; @@ -41,7 +44,7 @@ class BaseGetButtonsEvent extends AbstractEnvironmentAwareEvent * * @var string[] */ - protected $buttons; + protected $buttons = []; /** * Set the list of buttons. diff --git a/src/Contao/View/Contao2BackendView/Event/BuildWidgetEvent.php b/src/Contao/View/Contao2BackendView/Event/BuildWidgetEvent.php index d28e7bc8e..b4097c5df 100644 --- a/src/Contao/View/Contao2BackendView/Event/BuildWidgetEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/BuildWidgetEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -49,9 +50,9 @@ class BuildWidgetEvent extends AbstractModelAwareEvent /** * The instantiated widget. * - * @var Widget + * @var Widget|null */ - protected $widget; + protected $widget = null; /** * Create a new event. @@ -87,7 +88,7 @@ public function setWidget($widget) /** * Retrieve the widget instance from the event. * - * @return Widget + * @return Widget|null */ public function getWidget() { diff --git a/src/Contao/View/Contao2BackendView/Event/DecodePropertyValueForWidgetEvent.php b/src/Contao/View/Contao2BackendView/Event/DecodePropertyValueForWidgetEvent.php index 861d1ce14..4e579940d 100644 --- a/src/Contao/View/Contao2BackendView/Event/DecodePropertyValueForWidgetEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/DecodePropertyValueForWidgetEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -38,7 +39,7 @@ class DecodePropertyValueForWidgetEvent extends AbstractModelAwareEvent * * @var string */ - protected $property; + protected $property = ''; /** * The value of the data. @@ -58,10 +59,6 @@ class DecodePropertyValueForWidgetEvent extends AbstractModelAwareEvent */ public function setProperty($property) { - if (!\is_string($property)) { - throw new DcGeneralInvalidArgumentException('Only property name as string allowed.'); - } - $this->property = $property; return $this; diff --git a/src/Contao/View/Contao2BackendView/Event/EncodePropertyValueFromWidgetEvent.php b/src/Contao/View/Contao2BackendView/Event/EncodePropertyValueFromWidgetEvent.php index 217f6d47c..e320471b5 100644 --- a/src/Contao/View/Contao2BackendView/Event/EncodePropertyValueFromWidgetEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/EncodePropertyValueFromWidgetEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -40,7 +41,7 @@ class EncodePropertyValueFromWidgetEvent extends AbstractModelAwareEvent * * @var string */ - protected $property; + protected $property = ''; /** * The value of the data. diff --git a/src/Contao/View/Contao2BackendView/Event/GetBreadcrumbEvent.php b/src/Contao/View/Contao2BackendView/Event/GetBreadcrumbEvent.php index 794aff5b5..7b06d1886 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetBreadcrumbEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetBreadcrumbEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,7 +38,7 @@ class GetBreadcrumbEvent extends AbstractEnvironmentAwareEvent * * @var array */ - protected $elements; + protected $elements = []; /** * Set the breadcrumb elements to be displayed in the backend. diff --git a/src/Contao/View/Contao2BackendView/Event/GetGlobalButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/GetGlobalButtonEvent.php index cb5e8a914..7a7c80156 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetGlobalButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetGlobalButtonEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,21 +38,21 @@ class GetGlobalButtonEvent extends BaseButtonEvent * * @var string */ - protected $accessKey; + protected $accessKey = ''; /** * The css class to use. * * @var string */ - protected $class; + protected $class = ''; /** * The href to use. * * @var string */ - protected $href; + protected $href = ''; /** * Set the hotkey for the button. diff --git a/src/Contao/View/Contao2BackendView/Event/GetGroupHeaderEvent.php b/src/Contao/View/Contao2BackendView/Event/GetGroupHeaderEvent.php index d35a85788..933cc58d5 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetGroupHeaderEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetGroupHeaderEvent.php @@ -65,7 +65,7 @@ class GetGroupHeaderEvent extends AbstractModelAwareEvent /** * The value to be rendered. * - * @var string + * @var string|null */ protected $value; @@ -75,7 +75,7 @@ class GetGroupHeaderEvent extends AbstractModelAwareEvent * @param EnvironmentInterface $environment The environment. * @param ModelInterface $model The model being used as group header. * @param string $propertyName The name of the property being rendered into the group header. - * @param mixed $propertyValue The value of the property being rendered into the group header. + * @param string|null $propertyValue The value of the property being rendered into the group header. * @param string $groupingMode The grouping mode currently active. * @param int $groupingLength The grouping length currently active. */ @@ -146,7 +146,7 @@ public function setValue($value) /** * Retrieve the value to use in the group header. * - * @return string + * @return string|null */ public function getValue() { diff --git a/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php index a356f19e8..59e5d6abf 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php @@ -171,7 +171,7 @@ public function isCircularReference() /** * Set the next model in the list, succeeding the current model. * - * @param ModelInterface $next The successor. + * @param ModelInterface|null $next The successor. * * @return $this */ @@ -195,7 +195,7 @@ public function getNext() /** * Set the previous model in the list, preceding the current model. * - * @param ModelInterface $previous The id of the predecessor. + * @param ModelInterface|null $previous The id of the predecessor. * * @return $this */ diff --git a/src/Contao/View/Contao2BackendView/Event/GetParentHeaderEvent.php b/src/Contao/View/Contao2BackendView/Event/GetParentHeaderEvent.php index 24a68a782..defe94ef7 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetParentHeaderEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetParentHeaderEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Tristan Lins * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -38,7 +39,7 @@ class GetParentHeaderEvent extends AbstractModelAwareEvent * * @var array */ - protected $additional; + protected $additional = []; /** * Set the additional lines that shall be added to the header section. diff --git a/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php index c0e7a3c4c..dd9858e57 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -122,7 +123,7 @@ class GetPasteButtonEvent extends BaseButtonEvent */ public function setCircularReference($circularReference) { - $this->circularReference = (bool) $circularReference; + $this->circularReference = $circularReference; return $this; } diff --git a/src/Contao/View/Contao2BackendView/Event/GetPasteRootButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/GetPasteRootButtonEvent.php index dc2b93b30..fefdde37f 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetPasteRootButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetPasteRootButtonEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,14 +36,14 @@ class GetPasteRootButtonEvent extends BaseButtonEvent * * @var string */ - protected $href; + protected $href = ''; /** * Determinator if the paste button shall be disabled. * * @var bool */ - protected $pasteDisabled; + protected $pasteDisabled = false; /** * Set the href for the button. diff --git a/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php b/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php index c8c110dca..e860b0271 100644 --- a/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php @@ -46,20 +46,22 @@ class ModelToLabelEvent extends AbstractModelAwareEvent * The label information instance. * * @var ModelFormatterConfigInterface + * + * @psalm-suppress PropertyNotSetInConstructor */ protected $listLabel; /** * The arguments to use when building the label from the format string. * - * @var array + * @var array */ protected $args = []; /** * Set the arguments to use when generating the final string representation using the format string. * - * @param array $args The arguments. + * @param array $args The arguments. * * @return ModelToLabelEvent */ @@ -73,7 +75,7 @@ public function setArgs($args) /** * Retrieve the arguments to use when generating the final string representation using the format string. * - * @return array + * @return array */ public function getArgs() { diff --git a/src/Contao/View/Contao2BackendView/Event/ParentViewChildRecordEvent.php b/src/Contao/View/Contao2BackendView/Event/ParentViewChildRecordEvent.php index 8fb46f4ad..6e746e7dd 100644 --- a/src/Contao/View/Contao2BackendView/Event/ParentViewChildRecordEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/ParentViewChildRecordEvent.php @@ -37,7 +37,7 @@ class ParentViewChildRecordEvent extends AbstractModelAwareEvent * * @var string */ - protected $html; + protected $html = ''; /** * Set the html code to use as child record. diff --git a/src/Contao/View/Contao2BackendView/Event/PrepareMultipleModelsActionEvent.php b/src/Contao/View/Contao2BackendView/Event/PrepareMultipleModelsActionEvent.php index f55148f35..9fe5e74c3 100644 --- a/src/Contao/View/Contao2BackendView/Event/PrepareMultipleModelsActionEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/PrepareMultipleModelsActionEvent.php @@ -39,24 +39,24 @@ class PrepareMultipleModelsActionEvent extends AbstractActionAwareEvent /** * The model ids. * - * @var ModelIdInterface[] + * @var list */ - private $modelIds; + private array $modelIds; /** * The submit action. * * @var string */ - private $submitAction; + private string $submitAction; /** * Create a new instance. * - * @param EnvironmentInterface $environment The environment. - * @param Action $action The called action. - * @param ModelIdInterface[] $modelIds The list of model ids being parsed. - * @param string $submitAction The submit action name. + * @param EnvironmentInterface $environment The environment. + * @param Action $action The called action. + * @param list $modelIds The list of model ids being parsed. + * @param string $submitAction The submit action name. */ public function __construct(EnvironmentInterface $environment, Action $action, array $modelIds, $submitAction) { @@ -69,7 +69,7 @@ public function __construct(EnvironmentInterface $environment, Action $action, a /** * Get modelIds. * - * @return ModelIdInterface[] + * @return list */ public function getModelIds() { @@ -79,7 +79,7 @@ public function getModelIds() /** * Set the model ids. * - * @param ModelIdInterface[] $modelIds The new model ids. + * @param list $modelIds The new model ids. * * @return $this */ diff --git a/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php b/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php index 81c1fe374..1faba1d29 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,7 +25,10 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\System\GetReferrerEvent; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This handles the back button event in list views. @@ -52,6 +56,7 @@ public function handle(GetGlobalButtonEvent $event) $environment = $event->getEnvironment(); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if ( !( @@ -82,15 +87,21 @@ public function handle(GetGlobalButtonEvent $event) */ private function getReferrerUrl(EnvironmentInterface $environment) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $parent = $environment->getParentDataDefinition(); $event = new GetReferrerEvent( true, (null !== $parent) ? $parent->getName() - : $environment->getDataDefinition()->getName() + : $definition->getName() ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::SYSTEM_GET_REFERRER); + $dispatcher->dispatch($event, ContaoEvents::SYSTEM_GET_REFERRER); return $event->getReferrerUrl(); } diff --git a/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php b/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php index 7237f44a2..d075341a8 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php @@ -29,6 +29,8 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Widget Builder to append color picker wizards to Contao backend widgets. @@ -89,8 +91,11 @@ public static function getWizard($propInfo, EnvironmentInterface $environment) $wizard = ''; $propExtra = $propInfo->getExtra(); - if (\is_array($propExtra) && \array_key_exists('colorpicker', $propExtra) && $propExtra['colorpicker']) { - $pickerText = $environment->getTranslator()->translate('MSC.colorpicker'); + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + if (\array_key_exists('colorpicker', $propExtra) && $propExtra['colorpicker']) { + $pickerText = $translator->translate('MSC.colorpicker'); $event = new GenerateHtmlEvent( 'pickcolor.svg', $pickerText, @@ -102,7 +107,10 @@ public static function getWizard($propInfo, EnvironmentInterface $environment) ) ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::IMAGE_GET_HTML); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::IMAGE_GET_HTML); // Support single fields as well (see contao/core#5240) $strKey = $propExtra['multiple'] ? $propInfo->getName() . '_0' : $propInfo->getName(); @@ -112,7 +120,7 @@ public static function getWizard($propInfo, EnvironmentInterface $environment) 'id: "ctrl_%3$s", startColor: ((cl = $("ctrl_%3$s").value.hexToRgb(true)) ? cl : [255, 0, 0]),' . 'imgPath: "%4$s", onComplete: function(color) {$("ctrl_%3$s").value = color.hex.replace("#", "");}});' . '});', - $event->getHtml(), + $event->getHtml() ?? '', $propInfo->getName(), $strKey, 'assets/colorpicker/images/' diff --git a/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php b/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php index bba77a071..fd5d40e09 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,18 +13,21 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\EventListener; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; /** @@ -52,11 +55,18 @@ public function handle(GetGlobalButtonEvent $event) } $environment = $event->getEnvironment(); - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); - $mode = $basicDefinition->getMode(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + + $mode = $basicDefinition->getMode(); if (!$basicDefinition->isCreatable()) { $event->setHtml(''); + return; } @@ -65,21 +75,33 @@ public function handle(GetGlobalButtonEvent $event) || (BasicDefinitionInterface::MODE_HIERARCHICAL === $mode) ) { $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + + $provider = $basicDefinition->getDataProvider(); + assert(\is_string($provider)); + + $filter->andModelIsFromProvider($provider); if ($parentProviderName = $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } - if ($environment->getClipboard()->isNotEmpty($filter)) { + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + if ($clipboard->isNotEmpty($filter)) { $event->setHtml(''); + return; } } $url = UrlBuilder::fromUrl($event->getHref()); - if ($serializedPid = $environment->getInputProvider()->getParameter('pid')) { + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if ($serializedPid = $inputProvider->getParameter('pid')) { $url->setQueryParameter('pid', ModelId::fromSerialized($serializedPid)->getSerialized()); } diff --git a/src/Contao/View/Contao2BackendView/EventListener/CreateSubHeadlineListener.php b/src/Contao/View/Contao2BackendView/EventListener/CreateSubHeadlineListener.php index 955ebabb5..6665c7d1c 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/CreateSubHeadlineListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/CreateSubHeadlineListener.php @@ -20,6 +20,8 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\EventListener; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetEditMaskSubHeadlineEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * This class handles events to handle sub-headline at input mask. @@ -53,9 +55,15 @@ public function __invoke(GetEditMaskSubHeadlineEvent $event): void */ private function createSubHeadline(string $status, GetEditMaskSubHeadlineEvent $event): void { - $environment = $event->getEnvironment(); - $definitionName = $environment->getDataDefinition()->getName(); - $translator = $environment->getTranslator(); + $environment = $event->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $definitionName = $definition->getName(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $headline = $translator->translate($status, $definitionName, [$event->getModel()->getId()]); diff --git a/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php b/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php index 96031e3de..f143c04ca 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,7 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +22,8 @@ use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetSelectModeButtonsEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * This class handle for add the default buttons for the select mode. @@ -37,9 +39,15 @@ class SelectModeButtonsListener */ public function handleEvent(GetSelectModeButtonsEvent $event) { - $environment = $event->getEnvironment(); - $translator = $environment->getTranslator(); - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $environment = $event->getEnvironment(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); $buttons = []; $confirmMessage = \htmlentities( diff --git a/src/Contao/View/Contao2BackendView/FileSelect.php b/src/Contao/View/Contao2BackendView/FileSelect.php index 72d677b32..b3bd27c9d 100644 --- a/src/Contao/View/Contao2BackendView/FileSelect.php +++ b/src/Contao/View/Contao2BackendView/FileSelect.php @@ -37,12 +37,19 @@ use ContaoCommunityAlliance\DcGeneral\Contao\Callback\Callbacks; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\InputProvider; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneral; +use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\Translator\Contao\LangArrayTranslator; use ContaoCommunityAlliance\Translator\TranslatorChain; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class FileSelect. @@ -55,19 +62,12 @@ */ class FileSelect { - /** - * Current ajax object. - * - * @var object - */ - protected $objAjax; - /** * The DcGeneral Object. * - * @var DcGeneral + * @var DcGeneral|null */ - protected $itemContainer; + protected $itemContainer = null; /** * Initialize the controller. @@ -84,9 +84,11 @@ public function __construct() BackendUser::getInstance(); Config::getInstance(); Database::getInstance(); + /** @psalm-suppress DeprecatedMethod */ BackendUser::getInstance()->authenticate(); System::loadLanguageFile('default'); + /** @psalm-suppress DeprecatedMethod */ Backend::setStaticUrls(); } @@ -102,7 +104,8 @@ public function run() { $inputProvider = new InputProvider(); - $template = new BackendTemplate('be_picker'); + $template = new BackendTemplate('be_picker'); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->main = ''; $ajax = $this->runAjaxRequest(); @@ -111,7 +114,14 @@ public function run() $this->setupItemContainer($modelId); - $sessionStorage = $this->itemContainer->getEnvironment()->getSessionStorage(); + $itemContainer = $this->itemContainer; + assert($itemContainer instanceof DcGeneral); + + $environment = $itemContainer->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); // Define the current ID. \define( @@ -122,17 +132,31 @@ public function run() $fileSelector = $this->prepareFileSelector($modelId, $ajax); - $template->main = $fileSelector->generate(); - $template->theme = Backend::getTheme(); - $template->base = Environment::get('base'); - $template->language = $GLOBALS['TL_LANGUAGE']; - $template->title = StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['treepicker']); - $template->charset = $GLOBALS['TL_CONFIG']['characterSet']; - $template->addSearch = $fileSelector->searchField; - $template->search = $GLOBALS['TL_LANG']['MSC']['search']; - $template->action = StringUtil::ampersand(Environment::get('request')); - $template->value = $sessionStorage->get('file_selector_search'); - $template->manager = $GLOBALS['TL_LANG']['MSC']['treepickerManager']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->main = $fileSelector->generate(); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->theme = Backend::getTheme(); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->base = Environment::get('base'); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->language = $GLOBALS['TL_LANGUAGE']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->title = StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['treepicker']); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->charset = $GLOBALS['TL_CONFIG']['characterSet']; + /** + * @psalm-suppress UndefinedMagicPropertyAssignment + * @psalm-suppress UndefinedMagicPropertyFetch + */ + $template->addSearch = $fileSelector->searchField; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->search = $GLOBALS['TL_LANG']['MSC']['search']; + $template->action = StringUtil::ampersand(Environment::get('request')); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->value = $sessionStorage->get('file_selector_search'); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->manager = $GLOBALS['TL_LANG']['MSC']['treepickerManager']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->managerHref = ''; if ( @@ -141,23 +165,31 @@ public function run() ) { Backend::addFilesBreadcrumb('tl_files_picker'); } + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->breadcrumb = $GLOBALS['TL_DCA']['tl_files']['list']['sorting']['breadcrumb']; $user = BackendUser::getInstance(); // Add the manager link. + /** @psalm-suppress UndefinedMethod */ if ($user->hasAccess('files', 'modules')) { - $template->manager = $GLOBALS['TL_LANG']['MSC']['fileManager']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->manager = $GLOBALS['TL_LANG']['MSC']['fileManager']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->managerHref = 'contao/main.php?do=files&popup=1'; } + /** @psalm-suppress UndefinedMethod */ if (Input::get('switch') && $user->hasAccess('page', 'modules')) { - $template->switch = $GLOBALS['TL_LANG']['MSC']['pagePicker']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->switch = $GLOBALS['TL_LANG']['MSC']['pagePicker']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->switchHref = \str_replace('contao/file.php', 'contao/page.php', StringUtil::ampersand(Environment::get('request'))); } // Prevent debug output at all cost. $GLOBALS['TL_CONFIG']['debugMode'] = false; + /** @psalm-suppress DeprecatedMethod */ $template->output(); } @@ -173,7 +205,15 @@ private function getActiveModel(ModelIdInterface $modelId) if (!Database::getInstance()->tableExists($modelId->getDataProviderName())) { return null; } - $dataProvider = $this->itemContainer->getEnvironment()->getDataProvider(); + + $itemContainer = $this->itemContainer; + assert($itemContainer instanceof DcGeneral); + + $environment = $itemContainer->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); return $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); } @@ -184,13 +224,22 @@ private function getActiveModel(ModelIdInterface $modelId) * @param ModelIdInterface $modelId The model identifier. * @param Ajax $ajax The ajax request. * + * @psalm-suppress DeprecatedClass * @return FileSelector * * @SuppressWarnings(PHPMD.Superglobals) */ private function prepareFileSelector(ModelIdInterface $modelId, Ajax $ajax = null) { - $inputProvider = $this->itemContainer->getEnvironment()->getInputProvider(); + $itemContainer = $this->itemContainer; + assert($itemContainer instanceof DcGeneral); + + $environment = $itemContainer->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $propertyName = $inputProvider->getParameter('field'); $information = (array) $GLOBALS['TL_DCA'][$modelId->getDataProviderName()]['fields'][$propertyName]; @@ -198,22 +247,29 @@ private function prepareFileSelector(ModelIdInterface $modelId, Ajax $ajax = nul $information['eval'] = []; } + $itemContainer = $this->itemContainer; + assert($itemContainer instanceof DcGeneral); + + $definition = $itemContainer->getEnvironment(); + assert($definition instanceof ContainerInterface); + // Merge with the information from the data container. - $property = $this - ->itemContainer - ->getEnvironment() - ->getDataDefinition() - ->getPropertiesDefinition() - ->getProperty($propertyName); + $property = $definition->getPropertiesDefinition()->getProperty($propertyName); $extra = $property->getExtra(); $information['eval'] = \array_merge($extra, (array) $information['eval']); - $this->session->set('filePickerRef', Environment::get('request')); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + $sessionStorage->set('filePickerRef', Environment::get('request')); - $combat = new DcCompat($this->itemContainer->getEnvironment(), $this->getActiveModel($modelId), $propertyName); + $combat = new DcCompat($itemContainer->getEnvironment(), $this->getActiveModel($modelId), $propertyName); - /** @var FileSelector $fileSelector */ + /** + * @var FileSelector $fileSelector + * + * @psalm-suppress DeprecatedClass + */ $fileSelector = new $GLOBALS['BE_FFL']['fileSelector']( Widget::getAttributesFromDca( $information, @@ -247,7 +303,14 @@ private function prepareFileSelector(ModelIdInterface $modelId, Ajax $ajax = nul */ private function prepareValuesForFileSelector($propertyName, ModelIdInterface $modelId, DcCompat $combat) { - $inputProvider = $this->itemContainer->getEnvironment()->getInputProvider(); + $itemContainer = $this->itemContainer; + assert($itemContainer instanceof DcGeneral); + + $environment = $itemContainer->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $fileSelectorValues = []; foreach (\array_filter(\explode(',', $inputProvider->getParameter('value'))) as $k => $v) { @@ -273,7 +336,7 @@ private function prepareValuesForFileSelector($propertyName, ModelIdInterface $m } /** - * Setup the item container. + * Set up the item container. * * @param ModelIdInterface $modelId The model identifier. * @@ -284,7 +347,11 @@ private function prepareValuesForFileSelector($propertyName, ModelIdInterface $m private function setupItemContainer(ModelIdInterface $modelId) { $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); + $translator = new TranslatorChain(); + assert($translator instanceof TranslatorInterface); + $translator->add(new LangArrayTranslator($dispatcher)); $this->itemContainer = (new DcGeneralFactory()) diff --git a/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php b/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php index 7e05d27c2..5554fba2f 100644 --- a/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php +++ b/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author David Molineus * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2021 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,16 +26,24 @@ use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\ReloadEvent; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\Data\LanguageInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * Handler class for handling the show events. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class LanguageFilter implements EventSubscriberInterface { @@ -83,7 +91,7 @@ public function handleAction(ActionEvent $event) } /** - * Check if the data provider is multi language and prepare the data provider with the selected language. + * Check if the data provider is multi-language and prepare the data provider with the selected language. * * @param EnvironmentInterface $environment The environment. * @param bool $resetToFallback Flag if the language must be reset to the fallback. @@ -96,26 +104,45 @@ public function handleAction(ActionEvent $event) private function checkLanguage($environment, $resetToFallback) { $sessionStorage = $environment->getSessionStorage(); - $dataProvider = $environment->getDataProvider(); - $providerName = $environment->getDataDefinition()->getName(); - $modelId = $this->modelIdFromInput($environment->getInputProvider()); - $languages = $environment->getController()->getSupportedLanguages($modelId); + assert($sessionStorage instanceof SessionStorageInterface); + + $dataProvider = $environment->getDataProvider(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $providerName = $definition->getName(); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $modelId = $this->modelIdFromInput($inputProvider); + + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $languages = $controller->getSupportedLanguages($modelId); if (!$languages) { return; } - // Exit out when not a multi language provider. + // Exit out when not a multi-language provider. if (!($dataProvider instanceof MultiLanguageDataProviderInterface)) { return; } // If a new item, we MUST reset to the fallback as that is the first language that has to be stored // and set this language to session. + $session = []; if ((null === $modelId) && $resetToFallback) { - $dataProvider->setCurrentLanguage($dataProvider->getFallbackLanguage(null)->getLocale()); + $fallbackLanguage = $dataProvider->getFallbackLanguage(null); + assert($fallbackLanguage instanceof LanguageInformationInterface); + + $dataProvider->setCurrentLanguage($fallbackLanguage->getLocale()); $session['ml_support'][$providerName] = $dataProvider->getCurrentLanguage(); $sessionStorage->set('dc_general', $session); + return; } @@ -128,7 +155,10 @@ private function checkLanguage($environment, $resetToFallback) $currentLanguage = ($session['ml_support'][$providerName] ?? $GLOBALS['TL_LANGUAGE']); if (!\array_key_exists($currentLanguage, $languages)) { - $currentLanguage = $dataProvider->getFallbackLanguage($modelId)->getLocale(); + $fallbackLanguage = $dataProvider->getFallbackLanguage($modelId); + assert($fallbackLanguage instanceof LanguageInformationInterface); + + $currentLanguage = $fallbackLanguage->getLocale(); } $session['ml_support'][$providerName] = $currentLanguage; @@ -150,21 +180,33 @@ private function checkLanguage($environment, $resetToFallback) private function checkLanguageSubmit($environment, $languages) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if ('language_switch' !== $inputProvider->getValue('FORM_SUBMIT')) { return; } // Get/Check the new language. + $session = []; if ( $inputProvider->hasValue('language') && \array_key_exists($inputProvider->getValue('language'), $languages) ) { - $session['ml_support'][$environment->getDataDefinition()->getName()] = $inputProvider->getValue('language'); - $environment->getSessionStorage()->set('dc_general', $session); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $session['ml_support'][$definition->getName()] = $inputProvider->getValue('language'); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + $sessionStorage->set('dc_general', $session); } - $environment->getEventDispatcher()->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); } /** @@ -172,7 +214,7 @@ private function checkLanguageSubmit($environment, $languages) * * @param InputProviderInterface $inputProvider The input provider. * - * @return mixed|null + * @return ModelIdInterface|null */ private function modelIdFromInput(InputProviderInterface $inputProvider) { diff --git a/src/Contao/View/Contao2BackendView/GlobalButtonRenderer.php b/src/Contao/View/Contao2BackendView/GlobalButtonRenderer.php index 11f6e40d6..129169d63 100644 --- a/src/Contao/View/Contao2BackendView/GlobalButtonRenderer.php +++ b/src/Contao/View/Contao2BackendView/GlobalButtonRenderer.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author David Molineus - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,6 +28,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonsEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; @@ -67,8 +68,15 @@ class GlobalButtonRenderer public function __construct(EnvironmentInterface $environment) { $this->environment = $environment; - $this->dispatcher = $environment->getEventDispatcher(); - $this->translator = $environment->getTranslator(); + assert($environment instanceof EnvironmentInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $this->dispatcher = $dispatcher; + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $this->translator = $translator; } /** @@ -78,17 +86,13 @@ public function __construct(EnvironmentInterface $environment) */ public function render() { - /** @var CommandInterface[] $commands */ - $commands = $this - ->environment - ->getDataDefinition() - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME) - ->getGlobalCommands() - ->getCommands(); - - if (!\is_array($commands)) { - $commands = []; - } + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $commands = $backendView->getGlobalCommands()->getCommands(); $buttons = []; foreach ($commands as $command) { @@ -134,19 +138,19 @@ private function renderButton(CommandInterface $command) $href = $event->getUrl(); } - if (null === $label) { + if ('' === $label) { $label = $command->getName(); } $buttonEvent = new GetGlobalButtonEvent($this->environment); $buttonEvent - ->setAccessKey(isset($extra['accesskey']) ? \trim($extra['accesskey']) : null) + ->setAccessKey(isset($extra['accesskey']) ? \trim($extra['accesskey']) : '') ->setAttributes(' ' . \ltrim($extra['attributes'] ?? '')) ->setClass($extra['class']) ->setKey($command->getName()) ->setHref($href) ->setLabel($label) - ->setTitle($this->translate((string) $command->getDescription())); + ->setTitle($this->translate($command->getDescription())); $this->dispatcher->dispatch($buttonEvent, GetGlobalButtonEvent::NAME); // Allow to override the button entirely - if someone sets empty string, we keep it. @@ -174,7 +178,10 @@ private function renderButton(CommandInterface $command) */ private function translate($path) { - $value = $this->translator->translate($path, $this->environment->getDataDefinition()->getName()); + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $value = $this->translator->translate($path, $definition->getName()); if ($path !== $value) { return $value; } diff --git a/src/Contao/View/Contao2BackendView/PanelBuilder.php b/src/Contao/View/Contao2BackendView/PanelBuilder.php index b391f4e17..60e45f317 100644 --- a/src/Contao/View/Contao2BackendView/PanelBuilder.php +++ b/src/Contao/View/Contao2BackendView/PanelBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -21,6 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Panel\ElementInformationInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Panel\FilterElementInformationInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Panel\LimitElementInformationInterface; @@ -47,9 +49,9 @@ class PanelBuilder /** * The environment. * - * @var Contao2BackendViewDefinitionInterface + * @var EnvironmentInterface */ - private $environment; + private EnvironmentInterface $environment; /** * Create a new instance. @@ -71,15 +73,15 @@ public function build() $panel = new DefaultPanelContainer(); $panel->setEnvironment($this->environment); - /** @var Contao2BackendViewDefinitionInterface $viewDefinition */ - $viewDefinition = $this - ->environment - ->getDataDefinition() - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $viewDefinition = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($viewDefinition instanceof Contao2BackendViewDefinitionInterface); foreach ($viewDefinition->getPanelLayout()->getRows() as $panelKey => $row) { $panelRow = new DefaultPanel(); - $panel->addPanel($panelKey, $panelRow); + $panel->addPanel((string) $panelKey, $panelRow); foreach ($row as $element) { /** @var ElementInformationInterface $element */ if (null !== $instance = $this->createElement($element)) { diff --git a/src/Contao/View/Contao2BackendView/PanelRenderer.php b/src/Contao/View/Contao2BackendView/PanelRenderer.php index 31ecae19f..21c8246ac 100644 --- a/src/Contao/View/Contao2BackendView/PanelRenderer.php +++ b/src/Contao/View/Contao2BackendView/PanelRenderer.php @@ -29,8 +29,11 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPanelElementTemplateEvent; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelElementInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class renders a backend view panel including all elements. @@ -57,7 +60,7 @@ public function __construct($view) /** * Retrieve the environment. * - * @return EnvironmentInterface + * @return EnvironmentInterface|null */ protected function getEnvironment() { @@ -75,9 +78,13 @@ protected function getEnvironment() protected function renderPanelElement($element, $cssClass) { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $event = new GetPanelElementTemplateEvent($environment, $element); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); $template = $event->getTemplate(); @@ -105,7 +112,7 @@ protected function isIgnoredPanel(PanelElementInterface $element, $ignoredPanels return false; } - foreach ((array) $ignoredPanels as $class) { + foreach ($ignoredPanels as $class) { if ($element instanceof $class) { return true; } @@ -173,9 +180,13 @@ protected function renderPanelRow($panel, $ignoredPanels) public function render($ignoredPanels = []) { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); // If in edit/override all mode and list all properties, the panel filter isn´t in use. - if ('properties' === $environment->getInputProvider()->getParameter('select')) { + if ('properties' === $inputProvider->getParameter('select')) { return ''; } @@ -183,8 +194,11 @@ public function render($ignoredPanels = []) throw new DcGeneralRuntimeException('No panel information stored in data container.'); } + $panelContainer = $this->view->getPanel(); + assert($panelContainer instanceof PanelContainerInterface); + $panels = []; - foreach ($this->view->getPanel() as $panel) { + foreach ($panelContainer as $panel) { $panels[] = $this->renderPanelRow($panel, $ignoredPanels); } @@ -192,10 +206,13 @@ public function render($ignoredPanels = []) $template = new ContaoBackendViewTemplate('dcbe_general_panel'); $themeEvent = new GetThemeEvent(); - $environment->getEventDispatcher()->dispatch($themeEvent, ContaoEvents::BACKEND_GET_THEME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($themeEvent, ContaoEvents::BACKEND_GET_THEME); $template - ->set('action', StringUtil::ampersand($environment->getInputProvider()->getRequestUrl(), true)) + ->set('action', StringUtil::ampersand($inputProvider->getRequestUrl(), true)) ->set('theme', $themeEvent->getTheme()) ->set('panel', $panels); diff --git a/src/Contao/View/Contao2BackendView/ParentView.php b/src/Contao/View/Contao2BackendView/ParentView.php index ea164a21d..d8e03384a 100644 --- a/src/Contao/View/Contao2BackendView/ParentView.php +++ b/src/Contao/View/Contao2BackendView/ParentView.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,13 +16,15 @@ * @author Tristan Lins * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView; +use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\EnforceModelRelationshipEvent; use ContaoCommunityAlliance\DcGeneral\EventListener\ModelRelationship\ParentEnforcingListener; @@ -42,7 +44,10 @@ class ParentView extends BaseView */ public function enforceModelRelationship($model) { + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + // Fallback implementation. - (new ParentEnforcingListener())->process(new EnforceModelRelationshipEvent($this->getEnvironment(), $model)); + (new ParentEnforcingListener())->process(new EnforceModelRelationshipEvent($environment, $model)); } } diff --git a/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php b/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php index 57588b044..89011350a 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Christian Schiffler - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -126,7 +127,9 @@ public function checkPermissionIsEditable(BuildDataDefinitionEvent $event) return; } - $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($view instanceof Contao2BackendViewDefinitionInterface); + $modelCommands = $view->getModelCommands(); $this->disableCommandByActionName($modelCommands, 'edit'); @@ -153,7 +156,9 @@ public function checkPermissionIsDeletable(BuildDataDefinitionEvent $event) return; } - $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($view instanceof Contao2BackendViewDefinitionInterface); + $modelCommands = $view->getModelCommands(); $this->disableCommandByActionName($modelCommands, 'delete'); @@ -178,7 +183,9 @@ public function checkPermissionIsCreatable(BuildDataDefinitionEvent $event) return; } - $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($view instanceof Contao2BackendViewDefinitionInterface); + $modelCommands = $view->getModelCommands(); $this->disableCommandByActionName($modelCommands, 'copy'); @@ -210,7 +217,7 @@ private function getVisibilityConditionChain($property) /** * Disable command by action name. * - * @param CommandCollectionInterface $commands The commands collection. + * @param CommandCollectionInterface $commands The command's collection. * @param string $actionName The action name. * * @return void diff --git a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php index 3528fac10..711f68fb6 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php @@ -31,6 +31,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGroupHeaderEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; @@ -86,17 +87,18 @@ public function handle(GetGroupHeaderEvent $event) } $environment = $event->getEnvironment(); - $property = $environment - ->getDataDefinition() - ->getPropertiesDefinition() - ->getProperty($event->getGroupField()); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); // No property? Get out! - if (!$property) { + if (!$definition->getPropertiesDefinition()->hasProperty($event->getGroupField())) { $event->setValue('-'); return; } + $property = $definition->getPropertiesDefinition()->getProperty($event->getGroupField()); + $value = $this->formatGroupHeader( $environment, $event->getModel(), @@ -116,10 +118,10 @@ public function handle(GetGroupHeaderEvent $event) * @param EnvironmentInterface $environment The environment. * @param ModelInterface $model The model. * @param PropertyInterface $property The property. - * @param int $groupingMode The grouping mode. + * @param string $groupingMode The grouping mode. * @param int $groupingLength The grouping length. * - * @return string + * @return string|null */ protected function formatGroupHeader( EnvironmentInterface $environment, @@ -140,10 +142,10 @@ protected function formatGroupHeader( $value = ViewHelpers::getReadableFieldValue($environment, $property, $model); if (isset($evaluation['reference'])) { - $remoteNew = $evaluation['reference'][$value]; + $remoteNew = $evaluation['reference'][$value] ?? null; } elseif (ArrayUtil::isAssoc($property->getOptions())) { $options = $property->getOptions(); - $remoteNew = $options[$value]; + $remoteNew = $options[$value] ?? null; } else { $remoteNew = $value; } @@ -176,13 +178,13 @@ private function formatCheckboxOptionLabel($value) /** * Format the group header by the grouping mode. * - * @param int $groupingMode The grouping mode. + * @param string $groupingMode The grouping mode. * @param int $groupingLength The grouping length. * @param EnvironmentInterface $environment The environment. * @param PropertyInterface $property The current property definition. * @param ModelInterface $model The current data model. * - * @return string + * @return string|null */ private function formatByGroupingMode( $groupingMode, @@ -226,7 +228,7 @@ private function formatByCharGrouping($value, $groupingLength) return '-'; } - return mb_strtoupper(mb_substr($value, 0, $groupingLength ?: null)); + return \mb_strtoupper(\mb_substr($value, 0, $groupingLength ?: null)); } /** @@ -234,14 +236,16 @@ private function formatByCharGrouping($value, $groupingLength) * * @param int $value The value. * - * @return string + * @return string|null */ - private function formatByDayGrouping($value) + private function formatByDayGrouping(int $value): ?string { $value = $this->getTimestamp($value); - if ('' === $value) { + + if (0 === $value) { return '-'; } + $event = new ParseDateEvent($value, Config::get('dateFormat')); $this->dispatcher->dispatch($event, ContaoEvents::DATE_PARSE); @@ -253,12 +257,13 @@ private function formatByDayGrouping($value) * * @param int $value The value. * - * @return string + * @return string|null */ - private function formatByMonthGrouping($value) + private function formatByMonthGrouping(int $value): ?string { $value = $this->getTimestamp($value); - if ('' === $value) { + + if (0 === $value) { return '-'; } $event = new ParseDateEvent($value, 'F Y'); @@ -277,11 +282,12 @@ private function formatByMonthGrouping($value) private function formatByYearGrouping($value) { $value = $this->getTimestamp($value); - if ('' === $value) { + + if (0 === $value) { return '-'; } - return date('Y', $value); + return \date('Y', $value); } /** diff --git a/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php index 8e0164a47..87beaade0 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,26 +12,35 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber; +use Contao\Widget; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use MenAtWork\MultiColumnWizardBundle\Event\GetOptionsEvent; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * The multiple handler subscriber provides functions for edit/override all. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class MultipleHandlerSubscriber implements EventSubscriberInterface { @@ -63,7 +72,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) * * array('eventName' => array('methodName', $priority)) * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))) * - * @return array The event names to listen to + * @return array> */ public static function getSubscribedEvents() { @@ -96,7 +105,11 @@ public function prepareGlobalAllButton(ActionEvent $event) } $dataDefinition = $event->getEnvironment()->getDataDefinition(); - $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($dataDefinition instanceof ContainerInterface); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + $globalCommands = $backendView->getGlobalCommands(); if ($globalCommands->hasCommandNamed('all')) { @@ -108,7 +121,7 @@ public function prepareGlobalAllButton(ActionEvent $event) } /** - * Deactivate global button their are not useful. + * Deactivate global button there are not useful. * * @param ActionEvent $event The event. * @@ -125,7 +138,11 @@ public function deactivateGlobalButton(ActionEvent $event) } $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + $globalCommands = $backendView->getGlobalCommands(); $allowedButton = ['close_all_button']; @@ -153,10 +170,13 @@ public function handleOriginalOptions(GetOptionsEvent $event) { $environment = $event->getEnvironment(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + if ( - !$this->getScopeDeterminator()->currentScopeIsBackend() - || ('select' !== $environment->getInputProvider()->getParameter('act')) - || ('edit' !== $environment->getInputProvider()->getParameter('select')) + ('select' !== $inputProvider->getParameter('act')) + || ('edit' !== $inputProvider->getParameter('select')) + || !$this->getScopeDeterminator()->currentScopeIsBackend() ) { return; } @@ -184,7 +204,10 @@ public function handleOriginalOptions(GetOptionsEvent $event) $event->getOptions() ); - $environment->getEventDispatcher()->dispatch($originalOptionsEvent, GetOptionsEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($originalOptionsEvent, GetOptionsEvent::NAME); $event->setOptions($originalOptionsEvent->getOptions()); @@ -202,15 +225,21 @@ public function handleOriginalWidget(BuildWidgetEvent $event) { $environment = $event->getEnvironment(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + if ( - !$this->getScopeDeterminator()->currentScopeIsBackend() - || ('select' !== $environment->getInputProvider()->getParameter('act')) - || ('edit' !== $environment->getInputProvider()->getParameter('select')) + 'select' !== $inputProvider->getParameter('act') + || 'edit' !== $inputProvider->getParameter('select') + || !$this->getScopeDeterminator()->currentScopeIsBackend() ) { return; } - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); $this->findModelIdByPropertyName($event); @@ -218,10 +247,8 @@ public function handleOriginalWidget(BuildWidgetEvent $event) $modelId = ModelId::fromModel($model); $originalPropertyName = $this->getOriginalPropertyName($event->getProperty()->getName(), $modelId); - if ( - (null === $originalPropertyName) - || ((null !== $originalPropertyName) && (false === $properties->hasProperty($originalPropertyName))) - ) { + + if (null === $originalPropertyName) { return; } @@ -244,15 +271,22 @@ public function handleOriginalWidget(BuildWidgetEvent $event) $originalEvent = new BuildWidgetEvent($environment, $model, $originalProperty); - $environment->getEventDispatcher()->dispatch($originalEvent, BuildWidgetEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); - $originalEvent->getWidget()->id = $event->getProperty()->getName(); - $originalEvent->getWidget()->name = + $dispatcher->dispatch($originalEvent, BuildWidgetEvent::NAME); + + $widget = $originalEvent->getWidget(); + assert($widget instanceof Widget); + + $widget->id = $event->getProperty()->getName(); + $widget->name = \str_replace('::', '____', $modelId->getSerialized()) . '_[' . $originalPropertyName . ']'; - $originalEvent->getWidget()->tl_class = ''; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $widget->tl_class = ''; - $event->setWidget($originalEvent->getWidget()); + $event->setWidget($widget); $originalProperty->setExtra($originalExtra); @@ -272,24 +306,35 @@ private function findModelIdByPropertyName(BuildWidgetEvent $event) return; } - $environment = $event->getEnvironment(); + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); - $inputProvider = $environment->getInputProvider(); + assert($dataDefinition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + /** @var array{models: list} $session */ $session = $sessionStorage->get($dataDefinition->getName() . '.' . $inputProvider->getParameter('mode')); $model = null; foreach ($session['models'] as $sessionModel) { $model = $sessionModel; - if (0 !== \strpos($event->getProperty()->getName(), \str_replace('::', '____', $sessionModel))) { + if (!str_starts_with($event->getProperty()->getName(), \str_replace('::', '____', $sessionModel))) { continue; } break; } + if (null === $model) { + throw new DcGeneralException('Failed to find a model.'); + } + $modelId = ModelId::fromSerialized($model); $event->getModel()->setId($modelId->getId()); diff --git a/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php index 63d5d33d6..d3fe84007 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Christian Schiffler - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,6 +25,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\DecodePropertyValueForWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -64,7 +66,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) * * array('eventName' => array('methodName', $priority)) * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))) * - * @return array The event names to listen to + * @return array> */ public static function getSubscribedEvents() { @@ -93,7 +95,10 @@ public function convertFileSourceToUuid(EncodePropertyValueFromWidgetEvent $even return; } - $propertiesDefinition = $event->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + $definition = $event->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $propertiesDefinition = $definition->getPropertiesDefinition(); $property = $propertiesDefinition->getProperty($event->getProperty()); @@ -120,7 +125,10 @@ public function convertUuidToFileSource(DecodePropertyValueForWidgetEvent $event return; } - $propertiesDefinition = $event->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + $definition = $event->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $propertiesDefinition = $definition->getPropertiesDefinition(); $property = $propertiesDefinition->getProperty($event->getProperty()); diff --git a/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php b/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php index 16be0f3e5..a65e2a20e 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php @@ -43,16 +43,21 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Widget\PageTreeOrder; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Widget\TreePickerOrder; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentAwareInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Widget Builder build Contao backend widgets. * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @final */ class WidgetBuilder implements EnvironmentAwareInterface { @@ -74,6 +79,8 @@ class WidgetBuilder implements EnvironmentAwareInterface * Mapping list of widget types where the DC General has it own widgets. * * @var array + * + * @psalm-suppress DeprecatedClass - we know. :D */ protected static $widgetMapping = [ 'fileTree' => FileTree::class, @@ -112,8 +119,10 @@ public static function handleEvent(BuildWidgetEvent $event) return; } - $event - ->setWidget((new static($event->getEnvironment()))->buildWidget($event->getProperty(), $event->getModel())); + $widget = (new static($event->getEnvironment()))->buildWidget($event->getProperty(), $event->getModel()); + assert($widget instanceof Widget); + + $event->setWidget($widget); } /** @@ -131,7 +140,7 @@ public function getEnvironment() * * @param PropertyInterface $property The property to get the widget class name for. * - * @return string + * @return class-string * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) @@ -142,13 +151,9 @@ protected function getWidgetClass(PropertyInterface $property) return static::$widgetMapping[$property->getWidgetType()]; } - if (!isset($GLOBALS['BE_FFL'][$property->getWidgetType()])) { - return null; - } - - $className = $GLOBALS['BE_FFL'][$property->getWidgetType()]; + $className = $GLOBALS['BE_FFL'][$property->getWidgetType()] ?? ''; if (!\class_exists($className)) { - return null; + throw new DcGeneralRuntimeException('Failed to get widget class for ' . $property->getWidgetType()); } return $className; @@ -160,7 +165,7 @@ protected function getWidgetClass(PropertyInterface $property) * @param PropertyInterface $propInfo The property for which the X label shall be generated. * @param ModelInterface $model The model. * - * @return array + * @return array|null */ protected function getOptionsForWidget($propInfo, $model): ?array { @@ -173,7 +178,11 @@ protected function getOptionsForWidget($propInfo, $model): ?array $event = new GetPropertyOptionsEvent($environment, $model); $event->setPropertyName($propInfo->getName()); $event->setOptions($options); - $environment->getEventDispatcher()->dispatch($event, GetPropertyOptionsEvent::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, GetPropertyOptionsEvent::NAME); if ($event->getOptions() !== $options) { return $event->getOptions(); @@ -193,10 +202,9 @@ private function isGetOptionsAllowed(PropertyInterface $property): bool { $propExtra = $property->getExtra(); - // Check the overwrite param. + // Check to overwrite param. if ( - \is_array($propExtra) - && \array_key_exists('fetchOptions', $propExtra) + \array_key_exists('fetchOptions', $propExtra) && (true === $propExtra['fetchOptions']) ) { return true; @@ -219,10 +227,19 @@ private function isGetOptionsAllowed(PropertyInterface $property): bool protected function getTableWizard() { $environment = $this->getEnvironment(); + $dispatcher = $environment->getEventDispatcher(); - $defName = $environment->getDataDefinition()->getName(); - $translator = $environment->getTranslator(); - $urlEvent = new AddToUrlEvent('key=table'); + assert($dispatcher instanceof EventDispatcherInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $defName = $definition->getName(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $urlEvent = new AddToUrlEvent('key=table'); $importTableEvent = new GenerateHtmlEvent( 'tablewizard.svg', @@ -261,9 +278,9 @@ protected function getTableWizard() ' %s %s%s', StringUtil::ampersand($urlEvent->getUrl()), StringUtil::specialchars($translator->translate('importTable.1', $defName)), - $importTableEvent->getHtml(), - $shrinkEvent->getHtml(), - $expandEvent->getHtml() + $importTableEvent->getHtml() ?? '', + $shrinkEvent->getHtml() ?? '', + $expandEvent->getHtml() ?? '' ); } @@ -275,9 +292,17 @@ protected function getTableWizard() protected function getListWizard() { $environment = $this->getEnvironment(); - $dispatcher = $environment->getEventDispatcher(); - $defName = $environment->getDataDefinition()->getName(); - $translator = $environment->getTranslator(); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $defName = $definition->getName(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $urlEvent = new AddToUrlEvent('key=list'); @@ -294,7 +319,7 @@ protected function getListWizard() ' %s', StringUtil::ampersand($urlEvent->getUrl()), StringUtil::specialchars($translator->translate('importList.1', $defName)), - $importListEvent->getHtml() + $importListEvent->getHtml() ?? '' ); } @@ -310,6 +335,7 @@ protected function getXLabel($propInfo) $xLabel = ''; $environment = $this->getEnvironment(); $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); // Toggle line wrap (textarea). if (('textarea' === $propInfo->getWidgetType()) && !\array_key_exists('rte', $propInfo->getExtra())) { @@ -323,9 +349,12 @@ protected function getXLabel($propInfo) ) ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::IMAGE_GET_HTML); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::IMAGE_GET_HTML); - $xLabel .= ' ' . $event->getHtml(); + $xLabel .= ' ' . ($event->getHtml() ?? ''); } $xLabel .= $this->getHelpWizard($propInfo); @@ -355,6 +384,8 @@ protected function getHelpWizard($propInfo) $helpWizard = ''; $environment = $this->getEnvironment(); $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + // Add the help wizard. if ($propInfo->getExtra() && \array_key_exists('helpwizard', $propInfo->getExtra())) { $event = new GenerateHtmlEvent( @@ -363,16 +394,22 @@ protected function getHelpWizard($propInfo) 'style="vertical-align:text-bottom;"' ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::IMAGE_GET_HTML); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::IMAGE_GET_HTML); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); $helpWizard .= \sprintf( ' %s', - $environment->getDataDefinition()->getName(), + $definition->getName(), $propInfo->getName(), StringUtil::specialchars($translator->translate('MSC.helpWizard')), - $event->getHtml() + $event->getHtml() ?? '' ); } @@ -385,7 +422,7 @@ protected function getHelpWizard($propInfo) * @param PropertyInterface $property The property. * @param ModelInterface $model The current model. * - * @return Widget + * @return Widget|null * * @throws DcGeneralRuntimeException When not running in TL_MODE BE. * @@ -413,14 +450,18 @@ public function buildWidget( $prepareAttributes = $this->prepareWidgetAttributes($model, $property); $widget = new $class($prepareAttributes, new DcCompat($environment, $model, $property->getName())); + assert($widget instanceof Widget); // OH: what is this? source: DataContainer 232. $widget->currentRecord = $model->getId(); $widget->xlabel .= $this->getXLabel($property); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $event = new ManipulateWidgetEvent($environment, $model, $property, $widget); - $environment->getEventDispatcher()->dispatch($event, ManipulateWidgetEvent::NAME); + $dispatcher->dispatch($event, ManipulateWidgetEvent::NAME); return $widget; } @@ -442,7 +483,10 @@ private function valueToWidget(ModelInterface $model, PropertyInterface $propert ->setProperty($property->getName()) ->setValue($model->getProperty($property->getName())); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); $value = $event->getValue(); $propExtra = $property->getExtra(); @@ -472,11 +516,13 @@ private function valueToWidget(ModelInterface $model, PropertyInterface $propert private function prepareWidgetAttributes(ModelInterface $model, PropertyInterface $property) { $environment = $this->getEnvironment(); - $defName = $environment->getDataDefinition()->getName(); - $propExtra = $property->getExtra(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); - $value = $this->valueToWidget($model, $property); + $defName = $definition->getName(); + $propExtra = $property->getExtra(); + $value = $this->valueToWidget($model, $property); $propExtra['required'] = ('' === $value) && !empty($propExtra['mandatory']); @@ -505,7 +551,10 @@ private function prepareWidgetAttributes(ModelInterface $model, PropertyInterfac new DcCompat($environment, $model, $property->getName()) ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::WIDGET_GET_ATTRIBUTES_FROM_DCA); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::WIDGET_GET_ATTRIBUTES_FROM_DCA); $prepareAttributes = $event->getResult(); if ( diff --git a/src/Contao/View/Contao2BackendView/TreePicker.php b/src/Contao/View/Contao2BackendView/TreePicker.php index d01ad303a..ff8fd52ee 100644 --- a/src/Contao/View/Contao2BackendView/TreePicker.php +++ b/src/Contao/View/Contao2BackendView/TreePicker.php @@ -28,6 +28,7 @@ use Contao\Backend; use Contao\CoreBundle\Exception\ResponseException; +use Contao\CoreBundle\Picker\PickerBuilderInterface; use Contao\CoreBundle\Picker\PickerConfig; use Contao\StringUtil; use Contao\System; @@ -36,7 +37,11 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\ReloadEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; +use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\Contao\Factory\SessionStorageFactory; +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent; use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Controller\RelationshipManager; @@ -47,27 +52,40 @@ use ContaoCommunityAlliance\DcGeneral\Data\DCGE; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\DefaultModelFormatterConfig; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ListingConfigInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ModelFormatterConfigInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; use ContaoCommunityAlliance\DcGeneral\DC\General; +use ContaoCommunityAlliance\DcGeneral\DcGeneral; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Cmf\Component\Routing\ChainRouterInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Generator\UrlGenerator; /** * Provide methods to handle input field "tableTree". * + * @property array{fields: list, format: string, maxCharacters: int}|null $itemLabel + * @property string $searchField + * * @SuppressWarnings(PHPMD.TooManyPublicMethods) * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.TooManyMethods) * @SuppressWarnings(PHPMD.ExcessiveClassLength) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @psalm-suppress PropertyNotSetInConstructor */ class TreePicker extends Widget { @@ -92,27 +110,6 @@ class TreePicker extends Widget */ protected $subTemplate = 'widget_treepicker'; - /** - * The ajax id. - * - * @var string - */ - protected $strAjaxId; - - /** - * The ajax key. - * - * @var string - */ - protected $strAjaxKey; - - /** - * The ajax name. - * - * @var string - */ - protected $strAjaxName; - /** * The data Container. * @@ -125,7 +122,7 @@ class TreePicker extends Widget * * @var string */ - protected $sourceName; + protected $sourceName = ''; /** * The field type to use. @@ -136,13 +133,6 @@ class TreePicker extends Widget */ protected $fieldType = 'radio'; - /** - * Flag determining if the value shall always be saved. - * - * @var bool - */ - protected $alwaysSave; - /** * The minimum level for items to be selectable. * @@ -169,12 +159,12 @@ class TreePicker extends Widget * * @var string */ - protected $title; + protected $title = ''; /** * The data container for the item source. * - * @var General + * @var DcGeneral */ protected $itemContainer; @@ -183,28 +173,28 @@ class TreePicker extends Widget * * @var string */ - protected $orderField; + protected $orderField = ''; /** * The tree nodes to be handled. * - * @var TreeNodeStates + * @var TreeNodeStates|null */ - protected $nodeStates; + protected $nodeStates = null; /** * Create a new instance. * - * @param array $attributes The custom attributes. - * @param General $dataContainer The data container. + * @param array $attributes The custom attributes. + * @param General|null $dataContainer The data container. * - * @throws DcGeneralRuntimeException When not in Backend mode. */ public function __construct($attributes = [], General $dataContainer = null) { parent::__construct($attributes); $scopeDeterminator = System::getContainer()->get('cca.dc-general.scope-matcher'); + assert($scopeDeterminator instanceof RequestScopeDeterminator); if ($scopeDeterminator->currentScopeIsUnknown() || !$scopeDeterminator->currentScopeIsBackend()) { throw new DcGeneralRuntimeException('Treepicker is currently for Backend only.'); @@ -216,42 +206,59 @@ public function __construct($attributes = [], General $dataContainer = null) /** * Setup all local values and create the dc instance for the referenced data source. * - * @param General $dataContainer The data container to use. + * @param General|null $dataContainer The data container to use. * * @return void */ protected function setUp(General $dataContainer = null) { - $this->dataContainer = $dataContainer ?: $this->objDca; + if (null === $dataContainer) { + /** @var General|null $dataContainer */ + $dataContainer = $this->objDca; + } - if (!$this->dataContainer) { + if (!$dataContainer) { return; } + $this->dataContainer = $dataContainer; + $environment = $this->dataContainer->getEnvironment(); if (!$this->sourceName) { - $property = $environment - ->getDataDefinition() + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $property = $definition ->getPropertiesDefinition() - ->getProperty($environment->getInputProvider()->getValue('name')); + ->getProperty($inputProvider->getValue('name')); foreach ($property->getExtra() as $k => $v) { $this->$k = $v; } - $name = $environment->getInputProvider()->getValue('name'); + $name = $inputProvider->getValue('name'); $this->strField = $name; $this->strName = $name; $this->strId = $name; $this->label = $property->getLabel() ?: $name; - $this->strTable = $environment->getDataDefinition()->getName(); + $this->strTable = $definition->getName(); } + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + /** @psalm-suppress PropertyNotSetInConstructor */ $this->itemContainer = (new DcGeneralFactory()) ->setContainerName($this->sourceName) - ->setTranslator($environment->getTranslator()) - ->setEventDispatcher($environment->getEventDispatcher()) + ->setTranslator($translator) + ->setEventDispatcher($dispatcher) ->createDcGeneral(); } @@ -279,10 +286,16 @@ public function updateAjax($ajaxAction, $dataContainer) $this->setUp($dataContainer); $environment = $this->dataContainer->getEnvironment(); - $this->value = $environment->getInputProvider()->getValue('value'); - $label = $environment - ->getDataDefinition() + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $this->value = $inputProvider->getValue('value'); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $label = $definition ->getPropertiesDefinition() ->getProperty($this->strName) ->getDescription(); @@ -292,7 +305,7 @@ public function updateAjax($ajaxAction, $dataContainer) $result = '' . '

' . $this->generate(); - if (null !== $label && $GLOBALS['TL_CONFIG']['showHelp']) { + if ($GLOBALS['TL_CONFIG']['showHelp']) { $result .= '

' . $label . '

'; } @@ -302,7 +315,7 @@ public function updateAjax($ajaxAction, $dataContainer) /** * Retrieve the item container. * - * @return General + * @return DcGeneral */ public function getItemContainer() { @@ -327,25 +340,35 @@ public function getEnvironment() public function getTreeNodeStates() { if (!isset($this->nodeStates)) { - $environment = $this->getEnvironment(); - $sessionStorage = $environment->getSessionStorage(); + $environment = $this->getEnvironment(); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + $this->nodeStates = new TreeNodeStates( $sessionStorage->get($this->getToggleId()), $this->determineParentsOfValues() ); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + // Maybe it is not the best location to do this here. - if ('all' === $environment->getInputProvider()->getParameter('ptg')) { + if ('all' === $inputProvider->getParameter('ptg')) { // Save in session and reload. $sessionStorage->set( $this->getToggleId(), $this->nodeStates->setAllOpen($this->nodeStates->isAllOpen())->getStates() ); - $environment->getEventDispatcher()->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); } } + /** @psalm-suppress PropertyNotSetInConstructor */ return $this->nodeStates; } @@ -397,9 +420,9 @@ public function __set($key, $value) /** * Convert the value according to the configured fieldtype. * - * @param mixed $value The value to convert. + * @param null|list|string $value The value to convert. * - * @return mixed + * @return null|string|list * * @throws \RuntimeException When an unknown field type is encountered. */ @@ -418,8 +441,10 @@ private function convertValue($value) return $value; case 'checkbox': $delimiter = (false !== \stripos($value, "\t")) ? "\t" : ','; + /** @var array $files */ + $files = StringUtil::trimsplit($delimiter, $value); - return StringUtil::trimsplit($delimiter, $value); + return \array_values($files); default: } throw new \RuntimeException('Unknown field type encountered: ' . $this->fieldType); @@ -434,10 +459,13 @@ private function convertValue($value) * * fieldType : the input type. Either "radio" or "checkbox". * * titleIcon: the icon to use in the title section. * * mandatory: the field value must not be empty. + * * orderField: the field to order items. * * @param string $key The property name. * - * @return string The property value. + * @return mixed The property value. + * + * @psalm-suppress LessSpecificImplementedReturnType */ public function __get($key) { @@ -452,7 +480,7 @@ public function __get($key) return $this->titleIcon; case 'mandatory': - return $this->arrConfiguration['mandatory'] ?? false; + return $this->arrConfiguration['mandatory'] ?? ''; case 'orderField': return $this->orderField; @@ -462,27 +490,32 @@ public function __get($key) default: } + return parent::__get($key); } /** * Skip the field if "change selection" is not checked. * - * @param array $value The current value. + * @param null|string|list $varInput The current value. + * + * @return null|string|list * - * @return array|string + * @psalm-suppress MoreSpecificImplementedParamType */ - protected function validator($value) + protected function validator($varInput) { - $convertValue = $this->widgetToValue($value); + $convertValue = $this->widgetToValue($varInput); if ((null === $convertValue) && $this->mandatory) { $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); $message = empty($this->label) ? $translator->translate('ERR.mdtryNoLabel') : \sprintf( $translator->translate('ERR.mandatory'), + /** @psalm-suppress PropertyNotSetInConstructor */ $this->strLabel ); @@ -501,8 +534,9 @@ protected function validator($value) */ public function renderItemsPlain() { - $values = []; - $value = $this->varValue; + $values = []; + $value = $this->varValue; + /** @psalm-suppress UndefinedThisPropertyFetch */ $idProperty = $this->idProperty ?: 'id'; if ('radio' === $this->fieldType && !empty($value)) { @@ -511,8 +545,14 @@ public function renderItemsPlain() if (\is_array($value) && !empty($value)) { $environment = $this->getEnvironment(); - $dataDriver = $environment->getDataProvider(); - $config = $environment->getBaseConfigRegistry()->getBaseConfig(); + + $dataDriver = $environment->getDataProvider(); + assert($dataDriver instanceof DataProviderInterface); + + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $config = $registry->getBaseConfig(); $filter = FilterBuilder::fromArrayForRoot() ->getFilter() ->andPropertyValueIn($idProperty, $value) @@ -526,6 +566,10 @@ public function renderItemsPlain() } foreach ($dataDriver->fetchAll($config) as $model) { + if (!($model instanceof ModelInterface)) { + continue; + } + $formatted = $this->formatModel($model, false); $idValue = $model->getProperty($idProperty); $values[$idValue] = $formatted[0]['content']; @@ -580,16 +624,25 @@ public function generate() $GLOBALS['TL_JAVASCRIPT']['cca.dc-general.vanillaGeneral'] = 'bundles/ccadcgeneral/js/vanillaGeneral.js'; $environment = $this->getEnvironment(); - $translator = $environment->getTranslator(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $template = new ContaoBackendViewTemplate('widget_treepicker'); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $icon = new GenerateHtmlEvent($this->titleIcon); - $environment->getEventDispatcher()->dispatch($icon, ContaoEvents::IMAGE_GET_HTML); + $dispatcher->dispatch($icon, ContaoEvents::IMAGE_GET_HTML); $template ->setTranslator($translator) + /** @psalm-suppress PropertyNotSetInConstructor */ ->set('id', $this->strId) + /** @psalm-suppress PropertyNotSetInConstructor */ ->set('name', $this->strName) + /** @psalm-suppress PropertyNotSetInConstructor */ ->set('class', ($this->strClass ? ' ' . $this->strClass : '')) ->set('icon', $icon->getHtml()) ->set('title', $translator->translate($this->title ?: 'MSC.treePicker', null, [$this->sourceName])) @@ -618,14 +671,18 @@ public function generate() */ protected function generatePickerUrl() { + assert($this->dataContainer instanceof DcCompat); + $model = $this->dataContainer->getModel(); + assert($model instanceof ModelInterface); + $parameter = [ 'fieldType' => $this->fieldType, 'sourceName' => $this->sourceName, - 'modelId' => ModelId::fromModel($this->dataContainer->getModel())->getSerialized(), + 'modelId' => ModelId::fromModel($model)->getSerialized(), 'orderField' => $this->orderField, 'propertyName' => $this->name ]; - + /** @psalm-suppress UndefinedThisPropertyFetch */ if ($this->pickerOrderProperty && $this->pickerSortDirection) { $parameter = \array_merge( $parameter, @@ -636,15 +693,18 @@ protected function generatePickerUrl() ); } - return System::getContainer()->get('contao.picker.builder')->getUrl('cca_tree', $parameter); + $pickerBuilder = System::getContainer()->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + + return $pickerBuilder->getUrl('cca_tree', $parameter); } /** * Convert the value from widget for internal process. * - * @param mixed $value The widget value. + * @param null|string|list $value The widget value. * - * @return null|string|array + * @return null|string|list */ public function widgetToValue($value) { @@ -680,21 +740,31 @@ public function valueToWidget($value) */ protected function generateUpdateUrl() { - $request = System::getContainer()->get('request_stack')->getCurrentRequest(); + $requestStack = System::getContainer()->get('request_stack'); + assert($requestStack instanceof RequestStack); + + $request = $requestStack->getCurrentRequest(); + assert($request instanceof Request); + + $dataContainer = $this->dataContainer; + assert($dataContainer instanceof General); $configPicker = new PickerConfig( 'cca_tree', [ 'fieldType' => $this->fieldType, 'sourceName' => $this->sourceName, - 'modelId' => ModelId::fromModel($this->dataContainer->getModel())->getSerialized(), + 'modelId' => ModelId::fromModel($dataContainer->getModel())->getSerialized(), 'orderField' => $this->orderField, 'propertyName' => $this->name ], $this->valueToWidget($this->value) ); - return System::getContainer()->get('router')->generate( + $router = System::getContainer()->get('router'); + assert($router instanceof ChainRouterInterface); + + return $router->generate( 'cca_dc_general_tree_update', [ 'picker' => $configPicker->cloneForCurrent((string) $request->query->get('context'))->urlEncode() @@ -717,9 +787,16 @@ private function generateBreadCrumbUrl(ModelInterface $model) $toggleUrlEvent = new AddToUrlEvent( 'ptg=' . $model->getId() . '&provider=' . $model->getProviderName() ); - $this->getEnvironment()->getEventDispatcher()->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); - return System::getContainer()->get('router')->generate( + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); + + $router = System::getContainer()->get('router'); + assert($router instanceof ChainRouterInterface); + + return $router->generate( 'cca_dc_general_tree_breadcrumb', $this->getQueryParameterFromUrl($toggleUrlEvent->getUrl()), UrlGenerator::ABSOLUTE_URL @@ -738,9 +815,16 @@ private function generateToggleUrl(ModelInterface $model) $toggleUrlEvent = new AddToUrlEvent( 'ptg=' . $model->getId() . '&provider=' . $model->getProviderName() ); - $this->getEnvironment()->getEventDispatcher()->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); - return System::getContainer()->get('router')->generate( + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); + + $router = System::getContainer()->get('router'); + assert($router instanceof ChainRouterInterface); + + return $router->generate( 'cca_dc_general_tree_toggle', $this->getQueryParameterFromUrl($toggleUrlEvent->getUrl()), UrlGenerator::ABSOLUTE_URL @@ -756,7 +840,7 @@ private function generateToggleUrl(ModelInterface $model) */ private function getQueryParameterFromUrl($url) { - $parameters = array(); + $parameters = []; foreach (\preg_split('/&(amp;)?/i', \preg_split('/[?]/ui', $url)[1]) as $value) { $chunks = \explode('=', $value); $parameters[$chunks[0]] = $chunks[1]; @@ -779,7 +863,9 @@ private function addOrderFieldToTemplate(ContaoBackendViewTemplate $template) } $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + /** @psalm-suppress UndefinedThisPropertyFetch */ $template ->set('hasOrder', true) ->set('orderId', $this->orderField) @@ -802,16 +888,25 @@ public function generatePopup() $GLOBALS['TL_JAVASCRIPT']['cca.dc-general.vanillaGeneral'] = 'bundles/ccadcgeneral/js/vanillaGeneral.js'; $environment = $this->getEnvironment(); - $translator = $environment->getTranslator(); - $template = new ContaoBackendViewTemplate('widget_treepicker_popup'); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $template = new ContaoBackendViewTemplate('widget_treepicker_popup'); $icon = new GenerateHtmlEvent($this->titleIcon); - $environment->getEventDispatcher()->dispatch($icon, ContaoEvents::IMAGE_GET_HTML); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($icon, ContaoEvents::IMAGE_GET_HTML); $template ->setTranslator($translator) ->set('id', 'tl_listing') + /** @psalm-suppress PropertyNotSetInConstructor */ ->set('name', $this->strName) + /** @psalm-suppress PropertyNotSetInConstructor */ ->set('class', ($this->strClass ? ' ' . $this->strClass : '')) ->set('icon', $icon->getHtml()) ->set('title', $translator->translate($this->title ?: 'MSC.treePicker', null, [$this->sourceName])) @@ -841,6 +936,7 @@ public function generatePopup() */ private function getRootIds() { + /** @psalm-suppress UndefinedThisPropertyFetch */ $root = $this->root; $root = \is_array($root) ? $root : ((\is_numeric($root) && $root > 0) ? [$root] : []); $root = \array_merge($root, [0]); @@ -856,15 +952,22 @@ private function getRootIds() public function generateAjax() { $input = $this->getEnvironment()->getInputProvider(); + assert($input instanceof InputProviderInterface); if ($input->hasValue('action') && ('DcGeneralLoadSubTree' === $input->getValue('action'))) { $provider = $input->getValue('providerName'); $rootId = $input->getValue('id'); - $this->getEnvironment()->getSessionStorage()->set( + + $sessionStorage = $this->getEnvironment()->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + $sessionStorage->set( $this->getToggleId(), $this->getTreeNodeStates()->toggleModel($provider, $rootId)->getStates() ); + $collection = $this->loadCollection($rootId, ((int) $input->getValue('level') + 1)); + return $this->generateTreeView($collection, 'tree'); } @@ -878,7 +981,10 @@ public function generateAjax() */ protected function getToggleId() { - return $this->getEnvironment()->getDataDefinition()->getName() . $this->strId . '_tree'; + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition->getName() . $this->strId . '_tree'; } /** @@ -888,7 +994,10 @@ protected function getToggleId() */ public function getSearchSessionKey() { - return $this->getEnvironment()->getDataDefinition()->getName() . $this->strId . '_tree_search'; + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition->getName() . $this->strId . '_tree_search'; } /** @@ -932,8 +1041,12 @@ protected function determineModelState(ModelInterface $model, $level) */ protected function treeWalkModel(ModelInterface $model, $level, $subTables = []) { - $environment = $this->getEnvironment(); - $relationships = $environment->getDataDefinition()->getModelRelationshipDefinition(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); $hasChild = false; $this->determineModelState($model, ($level - 1)); @@ -949,12 +1062,15 @@ protected function treeWalkModel(ModelInterface $model, $level, $subTables = []) } // Create a new Config and fetch the children from the child provider. - $childConfig = $environment->getDataProvider($subTable)->getEmptyConfig(); + $dataProvider = $environment->getDataProvider($subTable); + assert($dataProvider instanceof DataProviderInterface); + + $childConfig = $dataProvider->getEmptyConfig(); $childConfig->setFilter($childFilter->getFilter($model)); $childConfig->setSorting(['sorting' => 'ASC']); - - $childCollection = $environment->getDataProvider($subTable)->fetchAll($childConfig); + $childCollection = $dataProvider->fetchAll($childConfig); + assert($childCollection instanceof CollectionInterface); $hasChild = ($childCollection->length() > 0); @@ -994,10 +1110,13 @@ protected function treeWalkModel(ModelInterface $model, $level, $subTables = []) */ private function treeWalkChildCollection(CollectionInterface $childCollection, ModelInterface $model, $level) { - $relationships = $this->getEnvironment()->getDataDefinition()->getModelRelationshipDefinition(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); foreach ($childCollection as $childModel) { - // Let the child know about it's parent. + // Let the child know about its parent. $model->setMeta($model::PARENT_ID, $model->getId()); $model->setMeta($model::PARENT_PROVIDER_NAME, $model->getProviderName()); @@ -1013,21 +1132,32 @@ private function treeWalkChildCollection(CollectionInterface $childCollection, M /** * Recursively retrieve a collection of all complete node hierarchy. * - * @param array $rootId The ids of the root node. - * @param int $level The level the items are residing on. - * @param string $providerName The data provider from which the root element originates from. + * @param mixed|null $rootId The ids of the root node. + * @param int $level The level the items are residing on. + * @param string $providerName The data provider from which the root element originates from. * * @return CollectionInterface */ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = null) { - $environment = $this->getEnvironment(); - $dataDriver = $environment->getDataProvider($providerName); + $environment = $this->getEnvironment(); + + $dataDriver = $environment->getDataProvider($providerName); + assert($dataDriver instanceof DataProviderInterface); + $tableTreeData = $dataDriver->getEmptyCollection(); - $rootConfig = $environment->getBaseConfigRegistry()->getBaseConfig(); - $relationships = $environment->getDataDefinition()->getModelRelationshipDefinition(); - if (!$rootId) { + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $rootConfig = $registry->getBaseConfig(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); + + if (null === $rootId) { $this->prepareFilterForRootCondition(); $this->pushRootModelToTreeCollection($dataDriver, $tableTreeData, $level); @@ -1037,6 +1167,7 @@ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = $rootConfig->setId($rootId); // Fetch root element. $rootModel = $dataDriver->fetch($rootConfig); + assert($rootModel instanceof ModelInterface); $mySubTables = []; foreach ($relationships->getChildConditions($rootModel->getProviderName()) as $condition) { @@ -1057,10 +1188,17 @@ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = */ private function prepareFilterForRootCondition() { - $environment = $this->getEnvironment(); - $rootCondition = $environment->getDataDefinition()->getModelRelationshipDefinition()->getRootCondition(); + $environment = $this->getEnvironment(); - $baseConfig = $environment->getBaseConfigRegistry()->getBaseConfig(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $rootCondition = $definition->getModelRelationshipDefinition()->getRootCondition(); + + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $baseConfig = $registry->getBaseConfig(); if (!$rootCondition) { return $baseConfig; } @@ -1089,12 +1227,19 @@ private function prepareFilterForRootCondition() private function pushRootModelToTreeCollection( DataProviderInterface $dataProvider, CollectionInterface $treeCollection, - $level + int $level ) { - $environment = $this->getEnvironment(); + $environment = $this->getEnvironment(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $baseConfig = $this->prepareFilterForRootCondition(); - $relationships = $environment->getDataDefinition()->getModelRelationshipDefinition(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); if ($inputProvider->hasParameter('orderProperty') && $inputProvider->hasParameter('sortDirection')) { $orderProperty = $inputProvider->getParameter('orderProperty'); @@ -1105,12 +1250,17 @@ private function pushRootModelToTreeCollection( // Fetch all root elements. $collection = $dataProvider->fetchAll($baseConfig); + assert($collection instanceof CollectionInterface); + if (!$collection->count()) { return; } + $firstModel = $collection->get(0); + assert($firstModel instanceof ModelInterface); + $mySubTables = []; - foreach ($relationships->getChildConditions($collection->get(0)->getProviderName()) as $condition) { + foreach ($relationships->getChildConditions($firstModel->getProviderName()) as $condition) { $mySubTables[] = $condition->getDestinationName(); } @@ -1124,9 +1274,9 @@ private function pushRootModelToTreeCollection( /** * Load the collection of child items and the parent item for the currently selected parent item. * - * @param mixed $rootId The root element (or null to fetch everything). - * @param int $level The current level in the tree (of the optional root element). - * @param null $providerName The data provider from which the optional root element shall be taken from. + * @param mixed|null $rootId The root element (or null to fetch everything). + * @param int $level The current level in the tree (of the optional root element). + * @param null $providerName The data provider from which the optional root element shall be taken from. * * @return CollectionInterface */ @@ -1136,9 +1286,14 @@ public function loadCollection($rootId = null, $level = 0, $providerName = null) $collection = $this->getTreeCollectionRecursive($rootId, $level, $providerName); - if ($rootId) { - $treeData = $environment->getDataProvider($providerName)->getEmptyCollection(); + if (null !== $rootId) { + $dataProvider = $environment->getDataProvider($providerName); + assert($dataProvider instanceof DataProviderInterface); + + $treeData = $dataProvider->getEmptyCollection(); $model = $collection->get(0); + assert($model instanceof ModelInterface); + foreach ($model->getMeta($model::CHILD_COLLECTIONS) as $collection) { foreach ($collection as $subModel) { $treeData->push($subModel); @@ -1162,7 +1317,12 @@ protected function getFormatter(ModelInterface $model, $treeMode) { /** @var ListingConfigInterface $listing */ $definition = $this->getEnvironment()->getDataDefinition(); - $listing = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME)->getListingConfig(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $listing = $backendView->getListingConfig(); if ($listing->hasLabelFormatter($model->getProviderName())) { return $listing->getLabelFormatter($model->getProviderName()); @@ -1170,7 +1330,7 @@ protected function getFormatter(ModelInterface $model, $treeMode) // If not in tree mode and custom label has been defined, use it. if (!$treeMode && $this->itemLabel) { - $label = (array) $this->itemLabel; + $label = $this->itemLabel; $formatter = new DefaultModelFormatterConfig(); $formatter->setPropertyNames($label['fields']); $formatter->setFormat($label['format']); @@ -1204,13 +1364,18 @@ protected function getFormatter(ModelInterface $model, $treeMode) public function formatModel(ModelInterface $model, $treeMode = true) { /** @var ListingConfigInterface $listing */ - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); - $listing = $definition - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME) - ->getListingConfig(); - $properties = $definition->getPropertiesDefinition(); - $defaultSortFields = \array_keys((array) $listing->getDefaultSortingFields()); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $listing = $backendView->getListingConfig(); + $properties = $definition->getPropertiesDefinition(); + /** @psalm-suppress DeprecatedMethod */ + $defaultSortFields = \array_keys($listing->getDefaultSortingFields()); $firstSorting = \reset($defaultSortFields); $formatter = $this->getFormatter($model, $treeMode); @@ -1229,7 +1394,10 @@ public function formatModel(ModelInterface $model, $treeMode = true) ->setLabel($formatter->getFormat()) ->setFormatter($formatter); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); $labelList = []; $this->prepareLabelWithDisplayedProperties($formatter, $event->getArgs(), $firstSorting, $labelList); @@ -1243,7 +1411,7 @@ public function formatModel(ModelInterface $model, $treeMode = true) * * @param ModelFormatterConfigInterface $formatter The model formatter. * @param array $arguments The model label arguments. - * @param string|bool $firstSorting The first sorting. + * @param bool|string $firstSorting The first sorting. * @param array $labelList The label list. * * @return void @@ -1251,11 +1419,16 @@ public function formatModel(ModelInterface $model, $treeMode = true) private function prepareLabelWithDisplayedProperties( ModelFormatterConfigInterface $formatter, array $arguments, - $firstSorting, + bool|string $firstSorting, array &$labelList ) { $definition = $this->getEnvironment()->getDataDefinition(); - $listing = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME)->getListingConfig(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $listing = $backendView->getListingConfig(); if (!$listing->getShowColumns()) { return; } @@ -1284,18 +1457,23 @@ private function prepareLabelWithDisplayedProperties( private function prepareLabelWithOutDisplayedProperties( ModelFormatterConfigInterface $formatter, array $arguments, - $label, + string $label, array &$labelList ) { $definition = $this->getEnvironment()->getDataDefinition(); - $listing = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME)->getListingConfig(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $listing = $backendView->getListingConfig(); if ($listing->getShowColumns()) { return; } $string = \vsprintf($label, $arguments); - if (($maxLength = null !== $formatter->getMaxLength()) && \strlen($string) > $maxLength) { + if ((null !== $maxLength = $formatter->getMaxLength()) && \strlen($string) > $maxLength) { $string = \substr($string, 0, $maxLength); } @@ -1318,10 +1496,13 @@ protected function parseModel($model, $toggleID) { $model->setMeta($model::LABEL_VALUE, $this->formatModel($model)); + $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + if ($model->getMeta($model::SHOW_CHILDREN)) { - $toggleTitle = $this->getEnvironment()->getTranslator()->translate('MSC.collapseNode'); + $toggleTitle = $translator->translate('MSC.collapseNode'); } else { - $toggleTitle = $this->getEnvironment()->getTranslator()->translate('MSC.expandNode'); + $toggleTitle = $translator->translate('MSC.expandNode'); } $toggleScript = \sprintf( @@ -1335,8 +1516,9 @@ protected function parseModel($model, $toggleID) ); $template = new ContaoBackendViewTemplate('widget_treepicker_entry'); + /** @psalm-suppress UndefinedThisPropertyFetch */ $template - ->setTranslator($this->getEnvironment()->getTranslator()) + ->setTranslator($translator) ->set('id', $this->strId) ->set('name', $this->strName) ->set('theme', Backend::getTheme()) @@ -1387,8 +1569,11 @@ protected function generateTreeView($collection, $treeClass) $subHtml .= $this->generateTreeView($objChildCollection, $treeClass); } + $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + $template - ->setTranslator($this->getEnvironment()->getTranslator()) + ->setTranslator($translator) ->set('objParentModel', $model) ->set('strToggleID', $toggleID) ->set('strHTML', $subHtml) @@ -1406,22 +1591,30 @@ protected function generateTreeView($collection, $treeClass) * Fetch all parents of the passed model. * * @param ModelInterface $model The model. - * @param string[] $parents The ids of all detected parents so far. + * @param array $parents The ids of all detected parents so far. * * @return void */ private function parentsOf($model, &$parents) { - $environment = $this->getEnvironment(); + $environment = $this->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $mode = $dataDefinition->getBasicDefinition()->getMode(); + assert(\is_int($mode)); + $collector = new ModelCollector($this->getEnvironment()); $relationships = new RelationshipManager( $dataDefinition->getModelRelationshipDefinition(), - $dataDefinition->getBasicDefinition()->getMode() + $mode ); if (!$relationships->isRoot($model)) { $parent = $collector->searchParentOf($model); + assert($parent instanceof ModelInterface); + if (!isset($parents[$model->getProviderName()][$parent->getId()])) { $this->parentsOf($parent, $parents); } @@ -1439,14 +1632,24 @@ private function determineParentsOfValues() { $parents = []; $environment = $this->getEnvironment(); - $mode = $environment->getDataDefinition()->getBasicDefinition()->getMode(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $mode = $definition->getBasicDefinition()->getMode(); + if (BasicDefinitionInterface::MODE_HIERARCHICAL !== $mode) { return []; } foreach ((array) $this->varValue as $value) { $dataDriver = $environment->getDataProvider(); - $this->parentsOf($dataDriver->fetch($dataDriver->getEmptyConfig()->setId($value)), $parents); + assert($dataDriver instanceof DataProviderInterface); + + $model = $dataDriver->fetch($dataDriver->getEmptyConfig()->setId($value)); + assert($model instanceof ModelInterface); + + $this->parentsOf($model, $parents); } return $parents; @@ -1459,31 +1662,41 @@ private function determineParentsOfValues() */ private function handleInputNameForEditAll() { + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + if ( - ('select' !== $this->getEnvironment()->getInputProvider()->getParameter('act')) - && ('edit' !== $this->getEnvironment()->getInputProvider()->getParameter('select')) - && ('edit' !== $this->getEnvironment()->getInputProvider()->getParameter('mode')) + ('select' !== $inputProvider->getParameter('act')) + && ('edit' !== $inputProvider->getParameter('select')) + && ('edit' !== $inputProvider->getParameter('mode')) ) { return; } - $tableName = \explode('____', $this->getEnvironment()->getInputProvider()->getValue('name'))[0]; + $tableName = \explode('____', $inputProvider->getValue('name'))[0]; $sessionKey = 'DC_GENERAL_' . \strtoupper($tableName); - $sessionStorage = - System::getContainer()->get('cca.dc-general.session_factory')->createService()->setScope($sessionKey); + $sessionFactory = System::getContainer()->get('cca.dc-general.session_factory'); + assert($sessionFactory instanceof SessionStorageFactory); - $selectAction = $this->getEnvironment()->getInputProvider()->getParameter('select'); + $sessionStorage = $sessionFactory->createService(); + assert($sessionStorage instanceof SessionStorageInterface); + $sessionStorage->setScope($sessionKey); + $selectAction = $inputProvider->getParameter('select'); + + /** @var array{models: list} $session */ $session = $sessionStorage->get($tableName . '.' . $selectAction); + $propertyNamePrefix = ''; $originalPropertyName = null; - foreach ((array) $session['models'] as $modelId) { + + foreach ($session['models'] as $modelId) { if (null !== $originalPropertyName) { break; } $propertyNamePrefix = \str_replace('::', '____', $modelId) . '_'; - if (0 !== strpos($this->strName, $propertyNamePrefix)) { + if (0 !== \strpos($this->strName, $propertyNamePrefix)) { continue; } diff --git a/src/Contao/View/Contao2BackendView/TreeSelect.php b/src/Contao/View/Contao2BackendView/TreeSelect.php index f67c0f887..2a752d705 100644 --- a/src/Contao/View/Contao2BackendView/TreeSelect.php +++ b/src/Contao/View/Contao2BackendView/TreeSelect.php @@ -33,11 +33,17 @@ use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber\WidgetBuilder; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneral; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\Translator\Contao\LangArrayTranslator; use ContaoCommunityAlliance\Translator\TranslatorChain; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class TreeSelect. @@ -45,16 +51,13 @@ * Back end tree picker for usage in generaltree.php. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @deprecated Do not use - here for legacy reasons only. + * + * @psalm-suppress PropertyNotSetInConstructor */ class TreeSelect { - /** - * Current ajax object. - * - * @var object - */ - protected $objAjax; - /** * The DcGeneral Object. * @@ -78,9 +81,11 @@ public function __construct() Config::getInstance(); Database::getInstance(); + /** @psalm-suppress DeprecatedMethod */ BackendUser::getInstance()->authenticate(); System::loadLanguageFile('default'); + /** @psalm-suppress DeprecatedMethod */ Backend::setStaticUrls(); } @@ -89,14 +94,20 @@ public function __construct() * * @return void * + * @throws \Exception * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function run() { - $environment = $this->itemContainer->getEnvironment(); + $environment = $this->itemContainer->getEnvironment(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); // Ajax request. // @codingStandardsIgnoreStart - We need POST access here. @@ -114,6 +125,7 @@ public function run() \define('CURRENT_ID', ($inputTable ? $sessionStorage->get('CURRENT_ID') : $inputId)); $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); $translator = new TranslatorChain(); $translator->add(new LangArrayTranslator($dispatcher)); @@ -131,8 +143,10 @@ public function run() } // Merge with the information from the data container. - $property = $environment - ->getDataDefinition() + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $property = $definition ->getPropertiesDefinition() ->getProperty($inputField); $extra = $property->getExtra(); @@ -142,14 +156,19 @@ public function run() $property->setExtra(\array_merge($property->getExtra(), $information['eval'])); $dataProvider = $environment->getDataProvider(); - $model = $dataProvider->getEmptyModel(); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->getEmptyModel(); if ($inputProvider->getParameter('id')) { $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($model instanceof ModelInterface); } + $widgetBuilder = new WidgetBuilder($environment); + /** @var \ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\TreePicker $treeSelector */ - $treeSelector = (new WidgetBuilder($environment))->buildWidget($property, $model); + $treeSelector = $widgetBuilder->buildWidget($property, $model); $treeSelector->value = \array_filter(\explode(',', $inputProvider->getParameter('value'))); // AJAX request. @@ -160,6 +179,7 @@ public function run() $template = new ContaoBackendViewTemplate('be_main'); + /** @psalm-suppress UndefinedMagicPropertyFetch */ $template ->set('isPopup', true) ->set('main', $treeSelector->generatePopup()) @@ -177,6 +197,10 @@ public function run() ->set('managerHref', ''); // Add the manager link. + /** + * @psalm-suppress UndefinedThisPropertyFetch + * @psalm-suppress UndefinedMagicPropertyFetch + */ if ($treeSelector->managerHref) { $template ->set('managerHref', 'contao?' . StringUtil::ampersand($treeSelector->managerHref) . '&popup=1'); diff --git a/src/Contao/View/Contao2BackendView/TreeView.php b/src/Contao/View/Contao2BackendView/TreeView.php index 3a05c6fb1..9f143ecf0 100644 --- a/src/Contao/View/Contao2BackendView/TreeView.php +++ b/src/Contao/View/Contao2BackendView/TreeView.php @@ -32,16 +32,21 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPasteRootButtonEvent; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Controller\TreeCollector; use ContaoCommunityAlliance\DcGeneral\Controller\TreeNodeStates; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; use ContaoCommunityAlliance\DcGeneral\DcGeneralViews; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; @@ -50,8 +55,13 @@ use ContaoCommunityAlliance\DcGeneral\Event\ViewEvent; use ContaoCommunityAlliance\DcGeneral\EventListener\ModelRelationship\TreeEnforcingListener; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\Panel\LimitElementInterface; +use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; use ContaoCommunityAlliance\DcGeneral\Panel\SortElementInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; @@ -94,6 +104,8 @@ public function __construct( if (null === $tokenManager) { $tokenManager = System::getContainer()->get('security.csrf.token_manager'); + assert($tokenManager instanceof CsrfTokenManagerInterface); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token manager as 2th argument to "' . __METHOD__ . '" is deprecated ' . @@ -104,6 +116,8 @@ public function __construct( } if (null === $tokenName) { $tokenName = System::getContainer()->getParameter('contao.csrf_token_name'); + assert(\is_string($tokenName)); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token name as 3th argument to "' . __METHOD__ . '" is deprecated ' . @@ -134,8 +148,13 @@ protected function getToggleId() */ protected function getTreeNodeStates() { - $sessionStorage = $this->getEnvironment()->getSessionStorage(); - $openElements = $sessionStorage->get($this->getToggleId()); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + $openElements = $sessionStorage->get($this->getToggleId()); if (!\is_array($openElements)) { $openElements = []; @@ -153,7 +172,12 @@ protected function getTreeNodeStates() */ protected function saveTreeNodeStates(TreeNodeStates $states) { - $sessionStorage = $this->getEnvironment()->getSessionStorage(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + $sessionStorage->set($this->getToggleId(), $states->getStates()); } @@ -166,7 +190,12 @@ protected function saveTreeNodeStates(TreeNodeStates $states) */ private function handleNodeStateChanges() { - $input = $this->getEnvironment()->getInputProvider(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + if (($modelId = $input->getParameter('ptg')) && ($providerName = $input->getParameter('provider'))) { $states = $this->getTreeNodeStates(); // Check if the open/close all has been triggered or just a model. @@ -179,7 +208,7 @@ private function handleNodeStateChanges() $this->toggleModel($providerName, $modelId); } - ViewHelpers::redirectHome($this->environment); + ViewHelpers::redirectHome($environment); } } @@ -219,27 +248,40 @@ protected function isModelOpen($model) */ public function loadCollection($rootId = null, $level = 0, $providerName = null) { - $environment = $this->getEnvironment(); - $dataDriver = $environment->getDataProvider($providerName); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dataDriver = $environment->getDataProvider($providerName); + assert($dataDriver instanceof DataProviderInterface); + + $panel = $this->getPanel(); + assert($panel instanceof PanelContainerInterface); + $realProvider = $dataDriver->getEmptyModel()->getProviderName(); - $collector = new TreeCollector( + + /** @psalm-suppress DeprecatedMethod */ + $collector = new TreeCollector( $environment, - $this->getPanel(), + $panel, $this->getViewSection()->getListingConfig()->getDefaultSortingFields(), $this->getTreeNodeStates() ); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $collection = $rootId ? $collector->getTreeCollectionRecursive($rootId, $level, $realProvider) : $collector->getChildrenOf( $realProvider, $level, - $environment->getInputProvider()->hasParameter('pid') ? $this->loadParentModel() : null + $inputProvider->hasParameter('pid') ? $this->loadParentModel() : null ); if ($rootId) { $treeData = $dataDriver->getEmptyCollection(); $model = $collection->get(0); + assert($model instanceof ModelInterface); if (!$model->getMeta(ModelInterface::HAS_CHILDREN)) { return $treeData; @@ -268,8 +310,12 @@ public function loadCollection($rootId = null, $level = 0, $providerName = null) protected function loadParentModel() { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); - if (!($parentId = $environment->getInputProvider()->getParameter('pid'))) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if (!($parentId = $inputProvider->getParameter('pid'))) { throw new DcGeneralRuntimeException( 'TreeView needs a proper parent id defined, somehow none is defined?', 1 @@ -318,23 +364,36 @@ protected function calcLabelFields($providerName) */ protected function parseModel($model, $toggleID) { - $event = new FormatModelLabelEvent($this->environment, $model); - $this->environment->getEventDispatcher()->dispatch($event, DcGeneralEvents::FORMAT_MODEL_LABEL); + $environment = $this->environment; + assert($environment instanceof EnvironmentInterface); + + $event = new FormatModelLabelEvent($environment, $model); + + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, DcGeneralEvents::FORMAT_MODEL_LABEL); $model->setMeta($model::LABEL_VALUE, $event->getLabel()); $template = $this->getTemplate('dcbe_general_treeview_entry'); + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + if ($model->getMeta($model::SHOW_CHILDREN)) { - $toggleTitle = $this->getEnvironment()->getTranslator()->translate('MSC.collapseNode'); + $toggleTitle = $translator->translate('MSC.collapseNode'); } else { - $toggleTitle = $this->getEnvironment()->getTranslator()->translate('MSC.expandNode'); + $toggleTitle = $translator->translate('MSC.expandNode'); } $toggleUrlEvent = new AddToUrlEvent( 'ptg=' . $model->getId() . '&provider=' . $model->getProviderName() ); - $this->getEnvironment()->getEventDispatcher()->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); + $dispatcher->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); $toggleData = [ 'url' => \html_entity_decode($toggleUrlEvent->getUrl()), @@ -377,9 +436,12 @@ protected function generateTreeView($collection, $treeClass) { $content = []; + $environment = $this->environment; + assert($environment instanceof EnvironmentInterface); + // Generate buttons - only if not in select mode! if (!$this->isSelectModeActive()) { - (new ButtonRenderer($this->environment))->renderButtonsForCollection($collection); + (new ButtonRenderer($environment))->renderButtonsForCollection($collection); } foreach ($collection as $model) { @@ -420,17 +482,30 @@ protected function generateTreeView($collection, $treeClass) */ public static function renderPasteRootButton(GetPasteRootButtonEvent $event) { - if (null !== $event->getHtml()) { - return $event->getHtml(); + if (null !== ($button = $event->getHtml())) { + return $button; } + $environment = $event->getEnvironment(); - $label = $environment->getTranslator()->translate( + assert($environment instanceof EnvironmentInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $label = $translator->translate( 'pasteinto.0', - $environment->getDataDefinition()->getName() + $definition->getName() ); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + if ($event->isPasteDisabled()) { /** @var GenerateHtmlEvent $imageEvent */ - $imageEvent = $environment->getEventDispatcher()->dispatch( + $imageEvent = $dispatcher->dispatch( new GenerateHtmlEvent( 'pasteinto_.svg', $label, @@ -439,11 +514,11 @@ public static function renderPasteRootButton(GetPasteRootButtonEvent $event) ContaoEvents::IMAGE_GET_HTML ); - return $imageEvent->getHtml(); + return $imageEvent->getHtml() ?? ''; } /** @var GenerateHtmlEvent $imageEvent */ - $imageEvent = $environment->getEventDispatcher()->dispatch( + $imageEvent = $dispatcher->dispatch( new GenerateHtmlEvent( 'pasteinto.svg', $label, @@ -457,7 +532,7 @@ public static function renderPasteRootButton(GetPasteRootButtonEvent $event) $event->getHref(), StringUtil::specialchars($label), 'onclick="Backend.getScrollOffset()"', - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -467,14 +542,20 @@ public static function renderPasteRootButton(GetPasteRootButtonEvent $event) * @param CollectionInterface $collection The collection of items. * * @return string + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function viewTree($collection) { $definition = $this->getDataDefinition(); $listing = $this->getViewSection()->getListingConfig(); $basicDefinition = $definition->getBasicDefinition(); - $environment = $this->getEnvironment(); - $dispatcher = $environment->getEventDispatcher(); + + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); // Init some Vars switch (6) { @@ -496,19 +577,26 @@ protected function viewTree($collection) if (null === $listing->getRootIcon()) { $labelIcon = 'pagemounts.svg'; } else { - $labelIcon = $listing->getRootIcon(); + $labelIcon = $listing->getRootIcon() ?? ''; } $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $filter->andModelIsFromProvider($dataProvider); if ($parentProviderName = $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + // Root paste into. - if ($environment->getClipboard()->isNotEmpty($filter)) { + if ($clipboard->isNotEmpty($filter)) { /** @var AddToUrlEvent $urlEvent */ $urlEvent = $dispatcher->dispatch( new AddToUrlEvent( @@ -520,7 +608,7 @@ protected function viewTree($collection) ContaoEvents::BACKEND_ADD_TO_URL ); - $buttonEvent = new GetPasteRootButtonEvent($this->getEnvironment()); + $buttonEvent = new GetPasteRootButtonEvent($environment); $buttonEvent ->setHref($urlEvent->getUrl()) ->setPasteDisabled(false); @@ -551,7 +639,7 @@ protected function viewTree($collection) $this->formActionForSelect($template); // Add breadcrumb, if we have one. - if (null !== ($breadcrumb = $this->breadcrumb())) { + if ('' !== ($breadcrumb = $this->breadcrumb())) { $template->set('breadcrumb', $breadcrumb); } @@ -568,12 +656,20 @@ protected function viewTree($collection) protected function formActionForSelect(ContaoBackendViewTemplate $template) { $environment = $this->getEnvironment(); - if (!$template->get('select') || ('select' !== $environment->getInputProvider()->getParameter('act'))) { + assert($environment instanceof EnvironmentInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if (!$template->get('select') || ('select' !== $inputProvider->getParameter('act'))) { return; } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $actionUrlEvent = new AddToUrlEvent('select=properties'); - $environment->getEventDispatcher()->dispatch($actionUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); + $dispatcher->dispatch($actionUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); $template->set('action', $actionUrlEvent->getUrl()); } @@ -581,17 +677,22 @@ protected function formActionForSelect(ContaoBackendViewTemplate $template) /** * {@inheritDoc} * + * @param ModelInterface $model + * * @deprecated Use ContaoCommunityAlliance\DcGeneral\EventListener\ModelRelationship\TreeEnforcingListener * - * @see \ContaoCommunityAlliance\DcGeneral\EventListener\ModelRelationship\TreeEnforcingListener + * @see TreeEnforcingListener * * @return void */ public function enforceModelRelationship($model) { + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + // Fallback implementation. $listener = new TreeEnforcingListener(); - $listener->process(new EnforceModelRelationshipEvent($this->getEnvironment(), $model)); + $listener->process(new EnforceModelRelationshipEvent($environment, $model)); } /** @@ -600,7 +701,12 @@ public function enforceModelRelationship($model) public function showAll(Action $action) { $environment = $this->getEnvironment(); - if ($environment->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->edit($action); } @@ -609,8 +715,11 @@ public function showAll(Action $action) $collection = $this->loadCollection(); $content = []; - $viewEvent = new ViewEvent($this->environment, $action, DcGeneralViews::CLIPBOARD, []); - $environment->getEventDispatcher()->dispatch($viewEvent, DcGeneralEvents::VIEW); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $viewEvent = new ViewEvent($environment, $action, DcGeneralViews::CLIPBOARD, []); + $dispatcher->dispatch($viewEvent, DcGeneralEvents::VIEW); // A list with ignored panels. $ignoredPanels = [ @@ -618,7 +727,7 @@ public function showAll(Action $action) SortElementInterface::class ]; - $content['language'] = $this->languageSwitcher($this->environment); + $content['language'] = $this->languageSwitcher($environment); $content['panel'] = $this->panel($ignoredPanels); $content['buttons'] = $this->generateHeaderButtons(); $content['clipboard'] = $viewEvent->getResponse(); @@ -628,7 +737,7 @@ public function showAll(Action $action) } /** - * Execute the multi language support. + * Execute the multi-language support. * * @param EnvironmentInterface $environment The environment. * @@ -645,10 +754,16 @@ private function languageSwitcher(EnvironmentInterface $environment) /** @var MultiLanguageDataProviderInterface $dataProvider */ + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + return $template - ->set('languages', $environment->getController()->getSupportedLanguages(null)) + ->set('languages', $controller->getSupportedLanguages(null)) ->set('language', $dataProvider->getCurrentLanguage()) - ->set('submit', $this->environment->getTranslator()->translate('MSC.showSelected')) + ->set('submit', $translator->translate('MSC.showSelected')) ->set('REQUEST_TOKEN', $this->tokenManager->getToken($this->tokenName)) ->parse(); } @@ -666,7 +781,11 @@ private function languageSwitcher(EnvironmentInterface $environment) */ public function handleAjaxCall() { - $input = $this->getEnvironment()->getInputProvider(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); if ('DcGeneralLoadSubTree' !== $input->getValue('action')) { parent::handleAjaxCall(); @@ -701,6 +820,8 @@ public function ajaxTreeView($rootId, $providerName, $level) $collection = $this->loadCollection($rootId, $level, $providerName); $treeClass = ''; + // The switch is already prepared for more modes. + /** @psalm-suppress TypeDoesNotContainType */ switch (6) { case 5: $treeClass = 'tree'; @@ -717,17 +838,27 @@ public function ajaxTreeView($rootId, $providerName, $level) } /** - * Get the the container of selections. + * Get the container of selections. * * @return array */ - private function getSelectContainer() + private function getSelectContainer(): array { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $sessionName = $definition->getName() . '.' . $inputProvider->getParameter('mode'); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); - $sessionName = $environment->getDataDefinition()->getName() . '.' . $inputProvider->getParameter('mode'); - if (!$environment->getSessionStorage()->has($sessionName)) { + if (!$sessionStorage->has($sessionName)) { return []; } @@ -736,7 +867,7 @@ private function getSelectContainer() return []; } - $session = $environment->getSessionStorage()->get($sessionName); + $session = $sessionStorage->get($sessionName); if (!\array_key_exists($selectAction, $session)) { return []; } diff --git a/src/Contao/View/Contao2BackendView/ViewHelpers.php b/src/Contao/View/Contao2BackendView/ViewHelpers.php index c92d088e0..ac64737ee 100644 --- a/src/Contao/View/Contao2BackendView/ViewHelpers.php +++ b/src/Contao/View/Contao2BackendView/ViewHelpers.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author David Molineus * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,18 +29,23 @@ use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ListingConfigInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelInterface; use ContaoCommunityAlliance\DcGeneral\Panel\SortElementInterface; use ContaoCommunityAlliance\DcGeneral\View\Event\RenderReadablePropertyValueEvent; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Helper class that provides static methods used in views. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ViewHelpers { @@ -48,24 +54,29 @@ class ViewHelpers * * @param EnvironmentInterface $environment The environment. * - * @return GroupAndSortingDefinitionInterface + * @return GroupAndSortingDefinitionInterface|null */ public static function getCurrentSorting(EnvironmentInterface $environment) { /** @var BackendViewInterface $view */ $view = $environment->getView(); - foreach ($view->getPanel() as $panel) { + $panelInterface = $view->getPanel(); + assert($panelInterface instanceof PanelContainerInterface); + + foreach ($panelInterface as $panel) { /** @var PanelInterface $panel */ $sort = $panel->getElement('sort'); - if ($sort) { - /** @var SortElementInterface $sort */ + if ($sort instanceof SortElementInterface) { return $sort->getSelectedDefinition(); } } + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $viewSection */ - $viewSection = $environment->getDataDefinition()->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $viewSection = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); $definition = $viewSection->getListingConfig()->getGroupAndSortingDefinition(); if ($definition->hasDefault()) { return $definition->getDefault(); @@ -76,25 +87,31 @@ public static function getCurrentSorting(EnvironmentInterface $environment) /** * {@inheritDoc} + * + * @return string|null */ public static function getManualSortingProperty(EnvironmentInterface $environment) { /** @var BackendViewInterface $view */ $view = $environment->getView(); + $panelInterface = $view->getPanel(); + assert($panelInterface instanceof PanelContainerInterface); + $definition = null; - foreach ($view->getPanel() as $panel) { + foreach ($panelInterface as $panel) { /** @var PanelInterface $panel */ $sort = $panel->getElement('sort'); - if ($sort) { - /** @var SortElementInterface $sort */ + if ($sort instanceof SortElementInterface) { $definition = $sort->getSelectedDefinition(); } } if (null === $definition) { + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $viewDefinition */ - $dataDefinition = $environment->getDataDefinition(); $viewDefinition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); $groupAndSorting = $viewDefinition->getListingConfig()->getGroupAndSortingDefinition(); @@ -206,7 +223,10 @@ public static function getReadableFieldValue( $model->getProperty($property->getName()) ); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); if (null !== $event->getRendered()) { return $event->getRendered(); @@ -225,9 +245,10 @@ public static function getReadableFieldValue( public static function redirectHome(EnvironmentInterface $environment) { $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); - if ($input->hasParameter('table') && ($hasParentId = $input->hasParameter('pid'))) { - if ($hasParentId) { + if ($input->hasParameter('table')) { + if ($input->hasParameter('pid')) { $event = new RedirectEvent( \sprintf( 'contao?do=%s&table=%s&pid=%s', @@ -254,6 +275,9 @@ public static function redirectHome(EnvironmentInterface $environment) ); } - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::CONTROLLER_REDIRECT); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::CONTROLLER_REDIRECT); } } diff --git a/src/Contao/View/Contao2BackendView/Widget/AbstractWidget.php b/src/Contao/View/Contao2BackendView/Widget/AbstractWidget.php index 4e227d986..cf279f894 100644 --- a/src/Contao/View/Contao2BackendView/Widget/AbstractWidget.php +++ b/src/Contao/View/Contao2BackendView/Widget/AbstractWidget.php @@ -30,6 +30,10 @@ * Abstract widget class as base for dc general backend widgets. * * This widget is only prepared to run in DcCompat mode! + * + * @psalm-suppress PropertyNotSetInConstructor + * + * @property ?DcCompat $objDca */ abstract class AbstractWidget extends Widget { @@ -64,7 +68,9 @@ public function __construct($attributes = null, DcCompat $dataContainer = null) { parent::__construct($attributes); - $this->dataContainer = $dataContainer ?: $this->objDca; + $dataContainer = $dataContainer ?: $this->objDca; + assert($dataContainer instanceof DcCompat); + $this->dataContainer = $dataContainer; } /** @@ -74,6 +80,7 @@ public function __construct($attributes = null, DcCompat $dataContainer = null) */ public function getEnvironment() { + assert($this->dataContainer instanceof DcCompat); return $this->dataContainer->getEnvironment(); } @@ -84,6 +91,7 @@ public function getEnvironment() */ public function getModel() { + assert($this->dataContainer instanceof DcCompat); return $this->dataContainer->getModel(); } } diff --git a/src/Contao/View/Contao2BackendView/Widget/FileTree.php b/src/Contao/View/Contao2BackendView/Widget/FileTree.php index 6b4b82eef..6cc9b5528 100644 --- a/src/Contao/View/Contao2BackendView/Widget/FileTree.php +++ b/src/Contao/View/Contao2BackendView/Widget/FileTree.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author Stefan Heimes * @author Ingolf Steinhardt * @author Sven Baumann - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,9 +25,12 @@ use Contao\Config; use Contao\CoreBundle\Exception\ResponseException; +use Contao\CoreBundle\Image\ImageFactoryInterface; +use Contao\CoreBundle\Picker\PickerBuilderInterface; use Contao\DataContainer; use Contao\Environment; use Contao\File; +use Contao\FileSelector; use Contao\FilesModel; use Contao\Image; use Contao\Image\ResizeConfiguration; @@ -36,21 +39,33 @@ use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; use Symfony\Component\HttpFoundation\Response; /** * File tree widget being compatible with the dc general. * - * @property string strField The field name. - * @property bool mandatory If true the field is required. - * @property bool multiple If true multiple values are allowed. - * @property bool isGallery If true the a image gallery is rendered. - * @property bool isDownloads If true only allowed download files are listed. + * @property string $strField The field name. + * @property bool $mandatory If true the field is required. + * @property bool $multiple If true multiple values are allowed. + * @property bool $isGallery If true the image gallery is rendered. + * @property bool $isDownloads If true only allowed download files are listed. + * @property string $fieldType Either 'radio' or 'checkbox'. + * @property bool|null $files Flag if files shall be shown. + * @property bool|null $filesOnly Flag if only files shall be selectable. + * @property string|null $path The root path. + * @property string|null $extensions The list of valid file extensions. * * @see https://github.com/contao/core/blob/master/system/modules/core/widgets/FileTree.php * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @psalm-suppress PropertyNotSetInConstructor + * + * @deprecated Deprecated since Contao 4.13 as the used FilePicker has been deprecated. Use the picker instead. */ class FileTree extends AbstractWidget { @@ -71,16 +86,16 @@ class FileTree extends AbstractWidget /** * The order field attribute name. * - * @var string. + * @var string */ - protected $orderField; + protected $orderField = ''; /** * The order field value. * - * @var array. + * @var null|list */ - protected $orderFieldValue; + protected $orderFieldValue = null; /** * The default width of the thumbnail. @@ -171,7 +186,9 @@ public function __set($strKey, $varValue) * * @param string $strKey The property name. * - * @return string The property value + * @return bool|int|null|object|string The property value + * + * @psalm-suppress ImplementedReturnTypeMismatch - Widget incorrectly annotates that it only returns string. */ public function __get($strKey) { @@ -199,7 +216,7 @@ public function __get($strKey) * * @param string $strKey The property name. * - * @return boolean True if the property exists + * @return bool True if the property exists */ public function __isset($strKey) { @@ -217,12 +234,12 @@ public function __isset($strKey) return isset($this->placeholderImage); default: - return parent::__get($strKey); + return (bool) parent::__get($strKey); } } /** - * Setup the file tree widget. + * Set up the file tree widget. * * @return void * @@ -234,75 +251,93 @@ private function setUp() // Load the fonts for the drag hint (see #4838) $GLOBALS['TL_CONFIG']['loadGoogleFonts'] = true; - $this->projectDir = System::getContainer()->getParameter('kernel.project_dir'); + $projectDir = System::getContainer()->getParameter('kernel.project_dir'); + assert(\is_string($projectDir)); + $this->projectDir = $projectDir; - if (!$this->dataContainer || !$this->orderField) { + if (!$this->orderField) { return; } - $value = $this->dataContainer->getModel()->getProperty($this->orderField); + assert($this->dataContainer instanceof DcCompat); + $model = $this->dataContainer->getModel(); + assert($model instanceof ModelInterface); + + $value = $model->getProperty($this->orderField); // support serialized values. if (!\is_array($value)) { $value = StringUtil::deserialize($value, true); + assert(\is_array($value)); } + $value = \array_values($value); + /** @var list $value */ - $this->orderId = $this->orderField . \str_replace($this->strField, '', $this->strId); - $this->orderFieldValue = (!empty($value) && \is_array($value)) ? \array_filter($value) : []; + $this->orderId = $this->orderField . \str_replace($this->strField, '', (string) $this->strId); + $this->orderFieldValue = \array_values(\array_filter($value)); } /** * Process the validation. * - * @param mixed $inputValue The input value. + * @param mixed $varInput The input value. * * @return array|string */ - protected function validator($inputValue) + protected function validator($varInput) { - if ('' === $inputValue) { + if ('' === $varInput) { if ($this->mandatory) { + $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + $this->addError( - $this->getEnvironment()->getTranslator()->translate('mandatory', 'ERR', [$this->strLabel]) + $translator->translate('mandatory', 'ERR', [$this->strLabel]) ); } return ''; } - $inputValue = \array_map('\Contao\StringUtil::uuidToBin', \array_filter(\explode(',', $inputValue))); + $varInput = \array_map('\Contao\StringUtil::uuidToBin', \array_filter(\explode(',', $varInput))); - return $this->multiple ? $inputValue : $inputValue[0]; + return $this->multiple ? $varInput : $varInput[0]; } /** * Render the file list. * * @param array $icons The generated icons. - * @param Collection|null $collection The files collection. + * @param Collection|null $collection The file's collection. * - * @param bool $followSubDirs If true subfolders get rendered. + * @param bool $followSubDirs If true sub-folders get rendered. * * @return void */ - private function renderList(array &$icons, Collection $collection = null, $followSubDirs = false) + private function renderList(array &$icons, Collection $collection = null, bool $followSubDirs = false) { if (!$collection) { return; } foreach ($collection->getModels() as $model) { + assert($model instanceof FilesModel); + $uuid = $model->uuid; + assert(is_string($uuid)); // File system and database seem not in sync if (!\file_exists($this->projectDir . '/' . $model->path)) { continue; } if (('folder' === $model->type) && $followSubDirs) { - $this->renderList($icons, FilesModel::findByPid($model->uuid)); + $files = FilesModel::findByPid($uuid); + assert($files instanceof Collection); + $this->renderList($icons, $files); continue; } + if (false !== ($icon = $this->renderIcon($model, $this->isGallery, $this->isDownloads))) { - $icons[\md5($model->uuid)] = ['uuid' => $model->uuid, 'image' => $icon]; + $icons[\md5($uuid)] = ['uuid' => $uuid, 'image' => $icon]; } } } @@ -397,14 +432,16 @@ private function generateGalleryImage(File $file, $info) ) ) { // Inline the image if no preview image will be generated (see #636). - if ( - $file->height !== null && $file->height <= $this->thumbnailHeight - && $file->width !== null && $file->width <= $this->thumbnailWidth - ) { + if ($file->height <= $this->thumbnailHeight && $file->width <= $this->thumbnailWidth) { $image = $file->dataUri; } else { $projectDir = System::getContainer()->getParameter('kernel.project_dir'); - $image = System::getContainer()->get('contao.image.image_factory')->create( + assert(\is_string($projectDir)); + + $imageFactory = System::getContainer()->get('contao.image.image_factory'); + assert($imageFactory instanceof ImageFactoryInterface); + + $image = $imageFactory->create( $projectDir . '/' . $file->path, [$this->thumbnailWidth, $this->thumbnailHeight, ResizeConfiguration::MODE_BOX] )->getUrl($projectDir); @@ -468,15 +505,18 @@ private function generateLink() $extras['filesOnly'] = (bool) $this->filesOnly; } - if ($this->path) { - $extras['path'] = (string) $this->path; + if (\is_string($this->path)) { + $extras['path'] = $this->path; } - if ($this->extensions) { - $extras['extensions'] = (string) $this->extensions; + if (\is_string($this->extensions)) { + $extras['extensions'] = $this->extensions; } - return System::getContainer()->get('contao.picker.builder')->getUrl('file', $extras); + $pickerBuilder = System::getContainer()->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + + return $pickerBuilder->getUrl('file', $extras); } /** @@ -489,8 +529,12 @@ public function generate() if (!empty($this->varValue)) { $files = FilesModel::findMultipleByUuids((array) $this->varValue); - $this->renderList($icons, $files, $this->isGallery || $this->isDownloads); - //dump($icons); + + if (null !== $files) { + assert($files instanceof Collection); + $this->renderList($icons, $files, $this->isGallery || $this->isDownloads); + } + $icons = $this->applySorting($icons); // Files can be null. @@ -501,12 +545,15 @@ public function generate() } } + $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + $content = (new ContaoBackendViewTemplate($this->subTemplate)) - ->setTranslator($this->getEnvironment()->getTranslator()) + ->setTranslator($translator) ->set('name', $this->strName) ->set('id', $this->strId) ->set('value', \implode(',', $values)) - ->set('hasOrder', $this->orderField != '' && \is_array($this->orderFieldValue)) + ->set('hasOrder', $this->orderField !== '' && \is_array($this->orderFieldValue)) ->set('icons', $icons) ->set('isGallery', $this->isGallery) ->set('orderId', $this->orderId) @@ -538,14 +585,20 @@ public function updateAjax($ajaxAction, DataContainer $dataContainer) return ''; } + assert($dataContainer instanceof DcCompat); $this->dataContainer = $dataContainer; $this->setUp(); - $environment = $this->dataContainer->getEnvironment(); + $environment = $this->dataContainer->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); - $inputProvider = $environment->getInputProvider(); - $propertyName = $inputProvider->getValue('name'); - $information = (array) $GLOBALS['TL_DCA'][$dataContainer->getName()]['fields'][$propertyName]; + assert($dataDefinition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $propertyName = $inputProvider->getValue('name'); + $information = (array) $GLOBALS['TL_DCA'][$dataContainer->getName()]['fields'][$propertyName]; // Merge with the information from the data container. $information['eval'] = \array_merge( @@ -555,10 +608,11 @@ public function updateAjax($ajaxAction, DataContainer $dataContainer) $combat = new DcCompat($environment, null, $propertyName); - /** @var \FileSelector $widgetClass */ + /** @var class-string $widgetClass */ + /** @psalm-suppress DeprecatedClass - we know we are deprecated ourselves. :D */ $widgetClass = $GLOBALS['BE_FFL']['fileSelector']; - /** @var \FileSelector $widget */ + /** @psalm-suppress UnsafeInstantiation - no better way to instantiate :( */ $widget = new $widgetClass( $widgetClass::getAttributesFromDca( $information, diff --git a/src/Contao/View/Contao2BackendView/Widget/FileTreeOrder.php b/src/Contao/View/Contao2BackendView/Widget/FileTreeOrder.php index 92c8567ec..c2559a483 100644 --- a/src/Contao/View/Contao2BackendView/Widget/FileTreeOrder.php +++ b/src/Contao/View/Contao2BackendView/Widget/FileTreeOrder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Sven Baumann * @author Christian Schiffler * @author Stefan Heimes - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +27,8 @@ * This widget is a supporting widget to store the file tree orderings. * * The ContaoWidgetManager does not allow input values without a widget. This is used as helper widget instead. + * + * @psalm-suppress PropertyNotSetInConstructor */ class FileTreeOrder extends AbstractWidget { @@ -39,11 +42,9 @@ class FileTreeOrder extends AbstractWidget /** * {@inheritdoc} */ - protected function validator($inputValue) + protected function validator($varInput) { - $inputValue = \array_map('\Contao\StringUtil::uuidToBin', \array_filter(\explode(',', $inputValue))); - - return $inputValue; + return \array_map('\Contao\StringUtil::uuidToBin', \array_filter(\explode(',', $varInput))); } /** diff --git a/src/Contao/View/Contao2BackendView/Widget/PageTree.php b/src/Contao/View/Contao2BackendView/Widget/PageTree.php index a267c7008..b57a30d0a 100644 --- a/src/Contao/View/Contao2BackendView/Widget/PageTree.php +++ b/src/Contao/View/Contao2BackendView/Widget/PageTree.php @@ -20,14 +20,20 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Widget; +use Contao\CoreBundle\Picker\PickerBuilderInterface; use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\TreePicker; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * Page tree widget being compatible with the dc general. * * @see https://github.com/contao/core/blob/master/system/modules/core/widgets/PageTree.php + * + * @psalm-suppress PropertyNotSetInConstructor */ class PageTree extends TreePicker { @@ -41,15 +47,17 @@ class PageTree extends TreePicker /** * Process the validation. * - * @param mixed $inputValue The input value. + * @param mixed $varInput The input value. + * + * @return null|string|list * - * @return array|string */ - protected function validator($inputValue) + protected function validator($varInput) { $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); - $widgetValue = $this->widgetToValue($inputValue); + $widgetValue = $this->widgetToValue($varInput); if ((null === $widgetValue) && $this->mandatory) { $this->addError($translator->translate('mandatory', 'ERR', [$this->strLabel])); } @@ -70,9 +78,14 @@ public function loadCollection($rootId = null, $level = 0, $providerName = null) { $collection = $this->getTreeCollectionRecursive($rootId, $level, $providerName); - $treeData = $this->getEnvironment()->getDataProvider($providerName)->getEmptyCollection(); + $dataProvider = $this->getEnvironment()->getDataProvider($providerName); + assert($dataProvider instanceof DataProviderInterface); + + $treeData = $dataProvider->getEmptyCollection(); if ($rootId) { $objModel = $collection->get(0); + assert($objModel instanceof ModelInterface); + foreach ($objModel->getMeta($objModel::CHILD_COLLECTIONS) as $childCollection) { foreach ($childCollection as $subModel) { $treeData->push($subModel); @@ -103,8 +116,9 @@ protected function generatePickerUrl() 'fieldType' => $this->fieldType ]; - return System::getContainer() - ->get('contao.picker.builder') - ->getUrl('page', $extra); + $pickerBuilder = System::getContainer()->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + + return $pickerBuilder->getUrl('page', $extra); } } diff --git a/src/Contao/View/Contao2BackendView/Widget/PageTreeOrder.php b/src/Contao/View/Contao2BackendView/Widget/PageTreeOrder.php index dcfea41ef..21e13c6c3 100644 --- a/src/Contao/View/Contao2BackendView/Widget/PageTreeOrder.php +++ b/src/Contao/View/Contao2BackendView/Widget/PageTreeOrder.php @@ -23,6 +23,8 @@ * This widget is a supporting widget to store the page tree orderings. * * The ContaoWidgetManager does not allow input values without a widget. This is used as helper widget instead. + * + * @psalm-suppress PropertyNotSetInConstructor */ class PageTreeOrder extends AbstractWidget { diff --git a/src/Contao/View/Contao2BackendView/Widget/TreePickerOrder.php b/src/Contao/View/Contao2BackendView/Widget/TreePickerOrder.php index ac9e77d11..9dd81a68c 100644 --- a/src/Contao/View/Contao2BackendView/Widget/TreePickerOrder.php +++ b/src/Contao/View/Contao2BackendView/Widget/TreePickerOrder.php @@ -23,6 +23,8 @@ * This widget is a supporting widget to store the page tree orderings. * * The ContaoWidgetManager does not allow input values without a widget. This is used as helper widget instead. + * + * @psalm-suppress PropertyNotSetInConstructor */ class TreePickerOrder extends AbstractWidget { @@ -45,9 +47,9 @@ public function generate() /** * {@inheritdoc} */ - protected function validator($inputValue) + protected function validator($varInput) { - return \array_filter(\explode(',', $inputValue)); + return \array_filter(\explode(',', $varInput)); } /** diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php index a71c85681..e9f77c11a 100644 --- a/src/ContaoManager/Plugin.php +++ b/src/ContaoManager/Plugin.php @@ -29,6 +29,7 @@ use ContaoCommunityAlliance\Contao\Bindings\CcaEventsContaoBindingsBundle; use ContaoCommunityAlliance\DcGeneral\CcaDcGeneralBundle; use ContaoCommunityAlliance\UrlBuilder\CcaUrlBuilderBundle; +use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Config\Loader\LoaderResolverInterface; use Symfony\Component\HttpKernel\KernelInterface; @@ -59,8 +60,8 @@ public function getBundles(ParserInterface $parser) */ public function getRouteCollection(LoaderResolverInterface $resolver, KernelInterface $kernel) { - return $resolver - ->resolve(__DIR__ . '/../Resources/config/routing.yml') - ->load(__DIR__ . '/../Resources/config/routing.yml'); + $loader = $resolver->resolve(__DIR__ . '/../Resources/config/routing.yml'); + assert($loader instanceof LoaderInterface); + return $loader->load(__DIR__ . '/../Resources/config/routing.yml'); } } diff --git a/src/Controller/Ajax.php b/src/Controller/Ajax.php index e32f2cbb1..d11c442dd 100644 --- a/src/Controller/Ajax.php +++ b/src/Controller/Ajax.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,25 +28,32 @@ use Contao\Database; use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\DataContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DC\General; use ContaoCommunityAlliance\DcGeneral\EnvironmentAwareInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; +use Symfony\Component\HttpFoundation\Session\SessionInterface; /** * Class Ajax - General purpose Ajax handler for "executePostActions" as we can not use the default Contao * handling. * * See Contao core issue #5957. https://github.com/contao/core/pull/5957 + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class Ajax implements EnvironmentAwareInterface { /** * The data container calling us. * - * @var DataContainerInterface + * @var null|DataContainerInterface */ - protected $objDc; + protected $objDc = null; /** * Create a new instance. @@ -58,10 +66,13 @@ public function __construct() /** * Get the data container. * - * @return DataContainerInterface. + * @return General */ protected function getDataContainer() { + if (!$this->objDc instanceof General) { + throw new \LogicException('Must be set first via ' . self::class . '::executePostActions().'); + } return $this->objDc; } @@ -83,7 +94,10 @@ public function getEnvironment() */ protected function getGet($key, $decodeEntities = false) { - return $this->getEnvironment()->getInputProvider()->getParameter($key, $decodeEntities); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return $inputProvider->getParameter($key, $decodeEntities); } /** @@ -92,11 +106,18 @@ protected function getGet($key, $decodeEntities = false) * @param string $key The key to retrieve. * @param bool $decodeEntities Decode the entities. * - * @return mixed + * @return string|null */ protected function getPost($key, $decodeEntities = false) { - return $this->getEnvironment()->getInputProvider()->getValue($key, $decodeEntities); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $value = $inputProvider->getValue($key, $decodeEntities); + if ($value === null) { + return null; + } + return (string) $value; } /** @@ -108,7 +129,7 @@ protected function getPost($key, $decodeEntities = false) */ protected function getAjaxId() { - return preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', $this->getPost('id')); + return preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', (string) $this->getPost('id')); } /** @@ -116,13 +137,16 @@ protected function getAjaxId() * * This method exits the script! * - * @return void + * @return never * * @throws ResponseException Throws a response exception. + * + * @deprecated */ protected function loadStructure() { // Method ajaxTreeView is in TreeView.php - watch out! + /** @psalm-suppress DeprecatedMethod */ $response = new Response( $this->getDataContainer()->ajaxTreeView($this->getAjaxId(), (int) $this->getPost('level')) ); @@ -135,7 +159,7 @@ protected function loadStructure() * * This method exits the script! * - * @return void + * @return never * * @throws ResponseException Throws a response exception. */ @@ -206,7 +230,10 @@ public function executePostActions(DataContainerInterface $container) $this->objDc = $container; - $action = $this->getEnvironment()->getInputProvider()->getValue('action'); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $action = $inputProvider->getValue('action'); if ( \in_array( @@ -258,16 +285,18 @@ public function executePostActions(DataContainerInterface $container) private function getActiveModel() { $input = $this->getEnvironment()->getInputProvider(); + assert($input instanceof InputProviderInterface); + if (false === $input->hasParameter('id')) { return null; } - $modelId = ModelId::fromSerialized($input->getParameter('id')); - $dataProvider = $this->getEnvironment()->getDataProvider($modelId->getDataProviderName()); + $modelId = ModelId::fromSerialized($input->getParameter('id')); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + $dataProvider = $this->getEnvironment()->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); - return $model; + return $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); } /** @@ -275,7 +304,7 @@ private function getActiveModel() * * Will get called from subclasses to have a central endpoint to exit the script. * - * @return void + * @return never * * @deprecated Deperecated since 2.1 and where remove in 3.0. Use own response exit. * @@ -287,8 +316,13 @@ protected function exitScript() @\trigger_error('Use own response exit!', E_USER_DEPRECATED); // @codingStandardsIgnoreEnd - $session = System::getContainer()->get('session'); - $sessionBag = $session->getBag('contao_backend')->all(); + $session = System::getContainer()->get('session'); + assert($session instanceof SessionInterface); + + $sessionBag = $session->getBag('contao_backend'); + assert($sessionBag instanceof AttributeBagInterface); + + $sessionBag = $sessionBag->all(); $user = BackendUser::getInstance(); diff --git a/src/Controller/Ajax3X.php b/src/Controller/Ajax3X.php index af0a2182e..5ddf58245 100644 --- a/src/Controller/Ajax3X.php +++ b/src/Controller/Ajax3X.php @@ -27,17 +27,21 @@ use Contao\CoreBundle\Exception\ResponseException; use Contao\Dbafs; +use Contao\FileSelector; use Contao\PageSelector; use Contao\StringUtil; use Contao\Widget; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\System\LogEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoWidgetManager; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Response; /** @@ -51,23 +55,29 @@ class Ajax3X extends Ajax /** * Get the widget instance. * - * @param string $fieldName The property name. - * @param string $serializedId The serialized id of the model. - * @param string $propertyValue The property value. + * @param string $fieldName The property name. + * @param string|null $serializedId The serialized id of the model. + * @param string $propertyValue The property value. * - * @return Widget + * @return Widget|null */ protected function getWidget($fieldName, $serializedId, $propertyValue) { - $environment = $this->getEnvironment(); - $property = $environment->getDataDefinition()->getPropertiesDefinition()->getProperty($fieldName); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $property = $definition->getPropertiesDefinition()->getProperty($fieldName); $propertyValues = new PropertyValueBag(); if (null !== $serializedId) { $model = $this->getModelFromSerializedId($serializedId); } else { $dataProvider = $environment->getDataProvider(); - $model = $dataProvider->getEmptyModel(); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->getEmptyModel(); } $widgetManager = new ContaoWidgetManager($environment, $model); @@ -77,10 +87,10 @@ protected function getWidget($fieldName, $serializedId, $propertyValue) $propertyValue = $this->getTreeValue($treeType, $propertyValue); if (('file' === $treeType) || ('page' === $treeType)) { $extra = $property->getExtra(); - if (\is_array($propertyValue) && !isset($extra['multiple'])) { + if (!isset($extra['multiple'])) { $propertyValue = $propertyValue[0]; } else { - $propertyValue = \implode(',', (array) $propertyValue); + $propertyValue = \implode(',', $propertyValue); } } @@ -102,17 +112,20 @@ protected function getWidget($fieldName, $serializedId, $propertyValue) protected function loadPagetree() { $environment = $this->getEnvironment(); - $input = $environment->getInputProvider(); - $session = $environment->getSessionStorage(); + $input = $environment->getInputProvider(); assert($input instanceof InputProviderInterface); + $session = $environment->getSessionStorage(); assert($session instanceof SessionStorageInterface); - $field = $input->getValue('field'); - $name = $input->getValue('name'); - $level = (int) $input->getValue('level'); - $rootId = $input->getValue('id'); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $field = $input->getValue('field'); + $name = $input->getValue('name'); + $level = (int) $input->getValue('level'); + $rootId = (string) $input->getValue('id'); $ajaxId = \preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', $rootId); $ajaxKey = \str_replace('_' . $ajaxId, '', $rootId); @@ -126,14 +139,20 @@ protected function loadPagetree() $nodes[$ajaxId] = (int) $input->getValue('state'); $session->set($ajaxKey, $nodes); - $arrData['strTable'] = $environment->getDataDefinition()->getName(); - $arrData['id'] = $ajaxName ?: $rootId; - $arrData['name'] = $name; + $arrData = [ + 'strTable' => $definition->getName(), + 'id' => $ajaxName ?: $rootId, + 'name' => $name, + ]; - /** @var PageSelector $widget */ + /** + * @psalm-suppress DeprecatedClass + * @var PageSelector $widget + */ $widget = new $GLOBALS['BE_FFL']['pageSelector']($arrData, $this->getDataContainer()); $widget->value = $this->getTreeValue('page', $input->getValue('value')); + /** @psalm-suppress InvalidArgument - rather pass it "as is", we do not trust Contao annotations. */ $response = new Response($widget->generateAjax($ajaxId, $field, $level)); throw new ResponseException($response); @@ -150,20 +169,30 @@ protected function loadPagetree() protected function loadFiletree() { $environment = $this->getEnvironment(); - $input = $environment->getInputProvider(); - $folder = $input->getValue('folder'); - $field = $input->getValue('field'); - $level = (int) $input->getValue('level'); + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $folder = $input->getValue('folder'); + $field = $input->getValue('field'); + $level = (int) $input->getValue('level'); + + $arrData = []; $arrData['strTable'] = $input->getParameter('table'); $arrData['id'] = $field; $arrData['name'] = $field; $arrData = \array_merge( - $environment->getDataDefinition()->getPropertiesDefinition()->getProperty($field)->getExtra(), + $definition->getPropertiesDefinition()->getProperty($field)->getExtra(), $arrData ); - /** @var \FileSelector $widget */ + /** + * @psalm-suppress DeprecatedClass + * @var FileSelector $widget + */ $widget = new $GLOBALS['BE_FFL']['fileSelector']($arrData, $this->getDataContainer()); $widget->value = $this->getTreeValue($field, $input->getValue('value')); @@ -185,23 +214,26 @@ protected function loadFiletree() * @param string $type Either "page" or "file". * @param string $value The value as comma separated list. * - * @return string The value array. + * @return list The value array. */ protected function getTreeValue($type, $value) { // Convert the selected values. - if ('' !== $value) { - $value = StringUtil::trimsplit("\t", $value); - - // Automatically add resources to the DBAFS. - if ('file' === $type) { - foreach ($value as $k => $v) { - $value[$k] = StringUtil::binToUuid(Dbafs::addResource(\urldecode($v))->uuid); - } + if ('' === $value) { + return []; + } + $value = StringUtil::trimsplit("\t", $value); + + // Automatically add resources to the DBAFS. + if ('file' === $type) { + foreach ($value as $k => $v) { + $uuid = Dbafs::addResource(\urldecode($v))->uuid; + assert(\is_string($uuid)); + $value[$k] = StringUtil::binToUuid($uuid); } } - return $value; + return array_values($value); } /** @@ -219,16 +251,25 @@ protected function getModelFromSerializedId($serializedId) { $modelId = ModelId::fromSerialized($serializedId); $dataProvider = $this->getEnvironment()->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); if (null === $model) { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $event = new LogEvent( 'A record with the ID "' . $serializedId . '" does not exist in "' . - $this->getEnvironment()->getDataDefinition()->getName() . '"', + $definition->getName() . '"', 'Ajax executePostActions()', 'ERROR' ); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, ContaoEvents::SYSTEM_LOG); + + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::SYSTEM_LOG); throw new ResponseException(new Response(Response::$statusTexts[400], 400)); } @@ -239,23 +280,33 @@ protected function getModelFromSerializedId($serializedId) /** * Reload the file tree. * - * @return void + * @return never * * @throws ResponseException Throws a response exception. */ protected function reloadTree() { - $input = $this->getEnvironment()->getInputProvider(); + $input = $this->getEnvironment()->getInputProvider(); + assert($input instanceof InputProviderInterface); + $serializedId = ($input->hasParameter('id') && $input->getParameter('id')) ? $input->getParameter('id') : null; - $value = $input->hasValue('value') ? $input->getValue('value', true) : null; + $value = $input->hasValue('value') ? $input->getValue('value', true) : ''; + + $fieldName = $this->getFieldName(); + assert(\is_string($fieldName)); + + $widget = $this->getWidget($fieldName, $serializedId, $value); + assert($widget instanceof Widget); - $this->generateWidget($this->getWidget($this->getFieldName(), $serializedId, $value)); + $this->generateWidget($widget); throw new ResponseException(new Response('')); } /** * {@inheritDoc} + * + * @return never */ protected function reloadPagetree() { @@ -264,6 +315,8 @@ protected function reloadPagetree() /** * {@inheritDoc} + * + * @return never */ protected function reloadFiletree() { @@ -274,13 +327,20 @@ protected function reloadFiletree() * {@inheritDoc} * * @throws ResponseException Throws a response exception. + * + * @return never */ protected function setLegendState() { $environment = $this->getEnvironment(); - $input = $environment->getInputProvider(); - $session = $environment->getSessionStorage(); - $states = $session->get('LEGENDS'); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + + $session = $environment->getSessionStorage(); + assert($session instanceof SessionStorageInterface); + + $states = $session->get('LEGENDS'); $states[$input->getValue('table')][$input->getValue('legend')] = (bool) $input->getValue('state'); $session->set('LEGENDS', $states); @@ -297,6 +357,7 @@ private function getFieldName() { $environment = $this->getEnvironment(); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $fieldName = $inputProvider->hasValue('name') ? $inputProvider->getValue('name') : null; if (null === $fieldName) { @@ -308,8 +369,12 @@ private function getFieldName() } $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + /** @var array{models: list} $session */ $session = $sessionStorage->get($dataDefinition->getName() . '.' . $inputProvider->getParameter('select')); $originalPropertyName = null; @@ -344,6 +409,7 @@ private function generateWidget(Widget $widget) { $environment = $this->getEnvironment(); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if (('select' !== $inputProvider->getParameter('act')) && ('edit' !== $inputProvider->getParameter('mode'))) { echo $widget->parse(); @@ -351,9 +417,15 @@ private function generateWidget(Widget $widget) return; } - $model = $environment->getDataProvider()->getEmptyModel(); + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->getEmptyModel(); $model->setProperty($widget->name, $widget->value); - echo (new ContaoWidgetManager($environment, $model))->getWidget($inputProvider->getValue('name'))->parse(); + $widget = (new ContaoWidgetManager($environment, $model))->getWidget($inputProvider->getValue('name')); + assert($widget instanceof Widget); + + echo $widget->parse(); } } diff --git a/src/Controller/BackendTreeController.php b/src/Controller/BackendTreeController.php index a1555437d..4d8f2a7fa 100644 --- a/src/Controller/BackendTreeController.php +++ b/src/Controller/BackendTreeController.php @@ -24,6 +24,9 @@ use Contao\Backend; use Contao\Config; +use Contao\Controller; +use Contao\CoreBundle\Framework\ContaoFramework; +use Contao\CoreBundle\Picker\PickerBuilderInterface; use Contao\CoreBundle\Picker\PickerInterface; use Contao\Environment; use Contao\StringUtil; @@ -33,14 +36,20 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoWidgetManager; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\TreePicker; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; -use ContaoCommunityAlliance\DcGeneral\DcGeneral; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\Translator\TranslatorInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; +use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Annotation\Route; @@ -55,13 +64,6 @@ class BackendTreeController implements ContainerAwareInterface { use ContainerAwareTrait; - /** - * The DcGeneral Object. - * - * @var DcGeneral - */ - private $itemContainer; - /** * Handles the installation process. * @@ -71,9 +73,7 @@ class BackendTreeController implements ContainerAwareInterface */ public function generalTreeAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); - - return $this->runBackendTree($this->container->get('request_stack')->getCurrentRequest()); + return $this->runBackendTree($this->initializeAndExtractRequest()); } /** @@ -85,9 +85,7 @@ public function generalTreeAction() */ public function generalTreeToggleAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); - - return $this->runBackendTreeToggle($this->container->get('request_stack')->getCurrentRequest()); + return $this->runBackendTreeToggle($this->initializeAndExtractRequest()); } /** @@ -99,9 +97,7 @@ public function generalTreeToggleAction() */ public function generalTreeBreadCrumbAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); - - return $this->runBackendTreeBreadCrumb($this->container->get('request_stack')->getCurrentRequest()); + return $this->runBackendTreeBreadCrumb($this->initializeAndExtractRequest()); } /** @@ -113,9 +109,32 @@ public function generalTreeBreadCrumbAction() */ public function generalTreeUpdateAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); + return $this->runBackendTreeUpdate($this->initializeAndExtractRequest()); + } + + private function initializeAndExtractRequest(): Request + { + $container = $this->container; + assert($container instanceof ContainerInterface); + + $framework = $container->get('contao.framework'); + assert($framework instanceof ContaoFramework); + + /** + * @psalm-suppress InternalMethod + * @var Controller $controller + */ + $controller = $framework->getAdapter(Controller::class); + /** @psalm-suppress DeprecatedMethod */ + $controller->setStaticUrls(); + + $requestStack = $container->get('request_stack'); + assert($requestStack instanceof RequestStack); + + $currentRequest = $requestStack->getCurrentRequest(); + assert($currentRequest instanceof Request); - return $this->runBackendTreeUpdate($this->container->get('request_stack')->getCurrentRequest()); + return $currentRequest; } /** @@ -131,15 +150,7 @@ public function generalTreeUpdateAction() */ private function runBackendTree(Request $request) { - if (null === ($request->query->get('picker'))) { - throw new \InvalidArgumentException('No picker was given here.'); - } - $picker = $this->container->get('contao.picker.builder')->createFromData($request->query->get('picker')); - - $treeSelector = $this->prepareTreeSelector($picker); - - $sessionBag = $this->container->get('session')->getBag('contao_backend'); - $sessionBag->set($treeSelector->getSearchSessionKey(), $picker->getConfig()->getValue()); + [$value, $treeSelector] = $this->getTemplateData($request); $template = new ContaoBackendViewTemplate('be_main'); $template @@ -153,7 +164,7 @@ private function runBackendTree(Request $request) ->set('addSearch', $treeSelector->searchField) ->set('search', $GLOBALS['TL_LANG']['MSC']['search']) ->set('action', StringUtil::ampersand($request->getUri())) - ->set('value', $sessionBag->get($treeSelector->getSearchSessionKey())) + ->set('value', $value) ->set('manager', $GLOBALS['TL_LANG']['MSC']['treepickerManager'] ?? '') ->set('breadcrumb', $GLOBALS['TL_DCA'][$treeSelector->foreignTable]['list']['sorting']['breadcrumb'] ?? ''); @@ -173,21 +184,13 @@ private function runBackendTree(Request $request) */ private function runBackendTreeBreadCrumb(Request $request) { - if (null === ($request->query->get('picker'))) { - throw new \InvalidArgumentException('No picker was given here.'); - } - $picker = $this->container->get('contao.picker.builder')->createFromData($request->query->get('picker')); - - $treeSelector = $this->prepareTreeSelector($picker); - $treeSelector->generatePopup(); - - $sessionBag = $this->container->get('session')->getBag('contao_backend'); - $sessionBag->set($treeSelector->getSearchSessionKey(), $picker->getConfig()->getValue()); + [, $treeSelector] = $this->getTemplateData($request); $message = ' The bread crumb method isn´ implement yet. '; + $treeSelector->generatePopup(); $template = new ContaoBackendViewTemplate('be_main'); $template ->set('isPopup', true) @@ -215,15 +218,7 @@ private function runBackendTreeBreadCrumb(Request $request) */ private function runBackendTreeToggle(Request $request) { - if (null === ($request->query->get('picker'))) { - throw new \InvalidArgumentException('No picker was given here.'); - } - $picker = $this->container->get('contao.picker.builder')->createFromData($request->query->get('picker')); - - $treeSelector = $this->prepareTreeSelector($picker); - - $sessionBag = $this->container->get('session')->getBag('contao_backend'); - $sessionBag->set($treeSelector->getSearchSessionKey(), $picker->getConfig()->getValue()); + [, $treeSelector] = $this->getTemplateData($request); $buffer = $treeSelector->generateAjax(); @@ -251,67 +246,107 @@ private function runBackendTreeUpdate(Request $request) throw new BadRequestHttpException('This request isn`t from type ajax.'); } - if (null === ($request->query->get('picker'))) { - throw new BadRequestHttpException('No picker was given here.'); - } - $picker = $this->container->get('contao.picker.builder')->createFromData($request->query->get('picker')); + [$value, , $picker] = $this->getTemplateData($request, true); - $treeSelector = $this->prepareTreeSelector($picker); + $modelId = ModelId::fromSerialized($picker->getConfig()->getExtra('modelId')); - $sessionBag = $this->container->get('session')->getBag('contao_backend'); - $sessionBag->set( - $treeSelector->getSearchSessionKey(), - $treeSelector->widgetToValue($request->request->get('value')) - ); + $container = $this->container; + assert($container instanceof ContainerInterface); + $translator = $container->get('cca.translator.contao_translator'); + assert($translator instanceof TranslatorInterface); - $modelId = ModelId::fromSerialized($picker->getConfig()->getExtra('modelId')); + $dispatcher = $container->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); $factory = new DcGeneralFactory(); $general = $factory ->setContainerName($modelId->getDataProviderName()) - ->setTranslator($this->container->get('cca.translator.contao_translator')) - ->setEventDispatcher($this->container->get('event_dispatcher')) + ->setTranslator($translator) + ->setEventDispatcher($dispatcher) ->createDcGeneral(); $dataProvider = $general->getEnvironment()->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); if (!($model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())))) { $model = $dataProvider->getEmptyModel(); } - $widgetValue = $treeSelector->widgetToValue($request->request->get('value')); - if (\is_array($widgetValue)) { + if (\is_array($value)) { $values = []; - // Clean keys the have empty value. - foreach ($widgetValue as $index => $value) { + // Clean keys they have empty value. + foreach ($value as $index => $val) { if ( - empty($value) + empty($val) // The first key entry has the value on, if the checkbox for all checked. - || ((0 === $index) && ('on' === $value)) + || ((0 === $index) && ('on' === $val)) ) { continue; } - $values[] = $value; + $values[] = $val; } - $widgetValue = $values; + $value = $values; } + $propertyName = $picker->getConfig()->getExtra('propertyName'); $propertyValues = new PropertyValueBag(); - $propertyValues->setPropertyValue($picker->getConfig()->getExtra('propertyName'), $widgetValue); - $general->getEnvironment()->getController()->updateModelFromPropertyBag($model, $propertyValues); + $propertyValues->setPropertyValue($propertyName, $value); + + $controller = $general->getEnvironment()->getController(); + assert($controller instanceof ControllerInterface); + + $controller->updateModelFromPropertyBag($model, $propertyValues); $widgetManager = new ContaoWidgetManager($general->getEnvironment(), $model); - $buffer = - $widgetManager->renderWidget($picker->getConfig()->getExtra('propertyName'), false, $propertyValues); + $buffer = $widgetManager->renderWidget($propertyName, false, $propertyValues); $response = new Response($buffer); - $response->headers->set('Content-Type', 'txt/html; charset=' . Config::get('characterSet')); + $response->headers->set('Content-Type', 'txt/html; charset=' . (Config::get('characterSet') ?? '')); return $response; } + /** + * @param Request $request The request to obtain information from. + * @param bool $valueFromRequest Flag if the value shall be read from the request. + * + * @return array{0: string|list, 1: TreePicker, 2: PickerInterface} + */ + private function getTemplateData(Request $request, bool $valueFromRequest = false): array + { + if ('' === ($getPicker = (string) $request->query->get('picker'))) { + throw new BadRequestHttpException('No picker was given here.'); + } + $container = $this->container; + assert($container instanceof ContainerInterface); + $pickerBuilder = $container->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + $picker = $pickerBuilder->createFromData($getPicker); + assert($picker instanceof PickerInterface); + $treeSelector = $this->prepareTreeSelector($picker); + $session = $container->get('session'); + assert($session instanceof SessionInterface); + $sessionBag = $session->getBag('contao_backend'); + assert($sessionBag instanceof AttributeBagInterface); + $value = $picker->getConfig()->getValue(); + if ($valueFromRequest) { + if (null !== $reqValue = $request->request->get('value')) { + $reqValue = (string) $reqValue; + } + $value = $treeSelector->widgetToValue($reqValue); + } + + $sessionBag->set($treeSelector->getSearchSessionKey(), $value); + + return [ + $sessionBag->get($treeSelector->getSearchSessionKey()), + $treeSelector, + $picker + ]; + } + /** * Prepare the tree selector. * @@ -335,21 +370,35 @@ private function prepareTreeSelector(PickerInterface $picker) throw new \InvalidArgumentException('The field name contains invalid characters'); } - $sessionBag = $this->container->get('session')->getBag('contao_backend'); + $container = $this->container; + assert($container instanceof ContainerInterface); + + $session = $container->get('session'); + assert($session instanceof SessionInterface); + + $sessionBag = $session->getBag('contao_backend'); + assert($sessionBag instanceof AttributeBagInterface); + // Define the current ID. \define('CURRENT_ID', ($table ? $sessionBag->get('CURRENT_ID') : $modelId->getId())); - $this->itemContainer = (new DcGeneralFactory()) + $translator = $container->get('cca.translator.contao_translator'); + assert($translator instanceof TranslatorInterface); + + $dispatcher = $container->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); + + $itemContainer = (new DcGeneralFactory()) ->setContainerName($modelId->getDataProviderName()) - ->setTranslator($this->container->get('cca.translator.contao_translator')) - ->setEventDispatcher($this->container->get('event_dispatcher')) + ->setTranslator($translator) + ->setEventDispatcher($dispatcher) ->createDcGeneral(); + $definition = $itemContainer->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + // Merge with the information from the data container. - $property = $this - ->itemContainer - ->getEnvironment() - ->getDataDefinition() + $property = $definition ->getPropertiesDefinition() ->getProperty($picker->getConfig()->getExtra('propertyName')); @@ -359,16 +408,20 @@ private function prepareTreeSelector(PickerInterface $picker) } $information['eval'] = array_merge($property->getExtra(), $information['eval']); - $treeSelector = new $GLOBALS['BE_FFL']['DcGeneralTreePicker']( + $dcCompat = new DcCompat($itemContainer->getEnvironment()); + /** @var class-string $class */ + $class = $GLOBALS['BE_FFL']['DcGeneralTreePicker']; + /** @psalm-suppress UnsafeInstantiation - No other way to instantiate. */ + $treeSelector = new $class( Widget::getAttributesFromDca( $information, $field, \array_filter(\explode(',', $picker->getConfig()->getValue())), $field, $table, - new DcCompat($this->itemContainer->getEnvironment()) + $dcCompat ), - new DcCompat($this->itemContainer->getEnvironment()) + $dcCompat ); $treeSelector->id = 'tl_listing'; diff --git a/src/Controller/ControllerInterface.php b/src/Controller/ControllerInterface.php index d8bf48ec8..8b309bf9c 100644 --- a/src/Controller/ControllerInterface.php +++ b/src/Controller/ControllerInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Tristan Lins * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -53,7 +53,7 @@ public function setEnvironment(EnvironmentInterface $environment); public function getEnvironment(); /** - * Handle a action within this environment. + * Handle an action within this environment. * * @param Action $action The action to be executed. * @@ -95,8 +95,8 @@ public function searchParentOf(ModelInterface $model); * (or originating table of the model, if no provider name has been given) for all levels and parent child * conditions. * - * @param ModelInterface $objModel The model to assemble children from. - * @param string $strDataProvider The name of the data provider to fetch children from. + * @param ModelInterface $model The model to assemble children from. + * @param string $providerName The name of the data provider to fetch children from. * * @return array * @@ -104,7 +104,7 @@ public function searchParentOf(ModelInterface $model); * * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::collectChildrenOf(). */ - public function assembleAllChildrenFrom($objModel, $strDataProvider = ''); + public function assembleAllChildrenFrom($model, $providerName = ''); /** * Update the current model from a post request. Additionally, trigger meta palettes, if installed. @@ -171,9 +171,9 @@ public function getModelFromClipboardItem(ItemInterface $item); /** * Retrieve models from the clipboard items. * - * @param array|ItemInterface[] $items The clipboard items. + * @param list $items The clipboard items. * - * @return CollectionInterface|ModelInterface[] + * @return CollectionInterface */ public function getModelsFromClipboardItems(array $items); @@ -184,21 +184,21 @@ public function getModelsFromClipboardItems(array $items); * * @param ModelIdInterface $parentModelId The optional parent id. If not given, the models must not have a parent. * - * @return CollectionInterface|\ContaoCommunityAlliance\DcGeneral\Data\ModelInterface[] + * @return CollectionInterface */ public function getModelsFromClipboard(ModelIdInterface $parentModelId = null); /** * Evaluate clipboard items, then return the corresponding models. * - * @param ModelIdInterface $source The source model id. - * @param ModelIdInterface $after The previous model id. - * @param ModelIdInterface $into The hierarchical parent model id. - * @param ModelIdInterface $parentModelId The parent model id. - * @param FilterInterface $filter Clipboard filter. - * @param array $items Write-back evaluated clipboard items. + * @param ModelIdInterface|null $source The source model id. + * @param ModelIdInterface|null $after The previous model id. + * @param ModelIdInterface|null $into The hierarchical parent model id. + * @param ModelIdInterface|null $parentModelId The parent model id. + * @param FilterInterface|null $filter Clipboard filter. + * @param array $items Write-back evaluated clipboard items. * - * @return CollectionInterface|ModelInterface[] + * @return CollectionInterface */ public function applyClipboardActions( ModelIdInterface $source = null, @@ -212,9 +212,9 @@ public function applyClipboardActions( /** * Paste the content of the clipboard onto the top. * - * @param CollectionInterface $models The models to be inserted. - * @param string $sortedBy The name of the sorting property. - * @param ModelIdInterface $parentId The parent model ID. + * @param CollectionInterface $models The models to be inserted. + * @param string $sortedBy The name of the sorting property. + * @param ModelIdInterface|null $parentId The parent model ID. * * @return void */ diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index b9ed23a19..c89b49ae0 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -33,6 +33,8 @@ namespace ContaoCommunityAlliance\DcGeneral\Controller; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\FilterInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Item; @@ -48,6 +50,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelManipulator; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; @@ -61,6 +64,8 @@ use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class serves as main controller class in dc general. @@ -73,6 +78,7 @@ * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.TooManyMethods) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @psalm-suppress MissingConstructor */ class DefaultController implements ControllerInterface { @@ -133,11 +139,17 @@ public function __call($name, $arguments) */ public function setEnvironment(EnvironmentInterface $environment) { - $this->environment = $environment; - $definition = $environment->getDataDefinition(); + $this->environment = $environment; + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $mode = $definition->getBasicDefinition()->getMode(); + assert(\is_int($mode)); + $this->relationshipManager = new RelationshipManager( $definition->getModelRelationshipDefinition(), - $definition->getBasicDefinition()->getMode() + $mode ); $this->modelCollector = new ModelCollector($this->environment); @@ -158,9 +170,13 @@ public function getEnvironment() public function handle(Action $action) { $event = new ActionEvent($this->getEnvironment(), $action); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, DcGeneralEvents::ACTION); - return $event->getResponse(); + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, DcGeneralEvents::ACTION); + + return (string) $event->getResponse(); } /** @@ -168,7 +184,7 @@ public function handle(Action $action) * * @deprecated Use \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::searchParentOfIn(). * - * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::searchParentOfIn(). + * @see ModelCollector::searchParentOfIn */ public function searchParentOfIn(ModelInterface $model, CollectionInterface $models) { @@ -179,7 +195,12 @@ public function searchParentOfIn(ModelInterface $model, CollectionInterface $mod ); // @codingStandardsIgnoreEnd - return $this->modelCollector->searchParentOfIn($model, $models); + $parent = $this->modelCollector->searchParentOfIn($model, $models); + if (null === $parent) { + throw new \RuntimeException('Not found'); + } + + return $parent; } /** @@ -189,7 +210,7 @@ public function searchParentOfIn(ModelInterface $model, CollectionInterface $mod * * @deprecated Use \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::searchParentOf(). * - * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::searchParentOf(). + * @see ModelCollector::searchParentOf */ public function searchParentOf(ModelInterface $model) { @@ -200,7 +221,12 @@ public function searchParentOf(ModelInterface $model) ); // @codingStandardsIgnoreEnd - return $this->modelCollector->searchParentOf($model); + $parent = $this->modelCollector->searchParentOf($model); + if (null === $parent) { + throw new \RuntimeException('Not found'); + } + + return $parent; } /** @@ -208,7 +234,7 @@ public function searchParentOf(ModelInterface $model) * * @deprecated Use \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::collectChildrenOf(). * - * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::collectChildrenOf(). + * @see ModelCollector::collectChildrenOf */ public function assembleAllChildrenFrom($model, $providerName = '') { @@ -225,17 +251,15 @@ public function assembleAllChildrenFrom($model, $providerName = '') /** * Retrieve all siblings of a given model. * - * @param ModelInterface $model The model for which the siblings shall be retrieved from. - * @param string|null $sortingProperty The property name to use for sorting. - * @param ModelIdInterface $parentId The (optional) parent id to use. + * @param ModelInterface $model The model for which the siblings shall be retrieved from. + * @param null $sortingProperty The property name to use for sorting. + * @param ModelIdInterface|null $parentId The (optional) parent id to use. * * @return CollectionInterface * - * @throws DcGeneralRuntimeException When no parent model can be located. - * * @deprecated Use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::collectSiblingsOf(). * - * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::collectSiblingsOf(). + * @see ModelCollector::collectSiblingsOf */ protected function assembleSiblingsFor( ModelInterface $model, @@ -260,15 +284,23 @@ protected function assembleSiblingsFor( * * @return CollectionInterface * - * @throws DcGeneralRuntimeException Unable to retrieve children in non hierarchical mode. + * @throws DcGeneralRuntimeException Unable to retrieve children in non-hierarchical mode. * @throws DcGeneralInvalidArgumentException Invalid configuration. Child condition must be defined. */ protected function assembleChildrenFor(ModelInterface $model, $sortingProperty = null) { - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); - $provider = $environment->getDataProvider($model->getProviderName()); - $config = $environment->getBaseConfigRegistry()->getBaseConfig(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $provider = $environment->getDataProvider($model->getProviderName()); + assert($provider instanceof DataProviderInterface); + + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $config = $registry->getBaseConfig(); $relationships = $definition->getModelRelationshipDefinition(); if (BasicDefinitionInterface::MODE_HIERARCHICAL !== $definition->getBasicDefinition()->getMode()) { @@ -284,10 +316,12 @@ protected function assembleChildrenFor(ModelInterface $model, $sortingProperty = $config->setFilter($condition->getFilter($model)); if ($sortingProperty) { - $config->setSorting([(string) $sortingProperty => 'ASC']); + $config->setSorting([$sortingProperty => 'ASC']); } - return $provider->fetchAll($config); + $childrenCollection = $provider->fetchAll($config); + assert($childrenCollection instanceof CollectionInterface); + return $childrenCollection; } /** @@ -295,11 +329,12 @@ protected function assembleChildrenFor(ModelInterface $model, $sortingProperty = */ public function updateModelFromPropertyBag($model, $propertyValues) { - if (!$propertyValues) { - return $this; - } $environment = $this->getEnvironment(); - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); ModelManipulator::updateModelFromPropertyBag($properties, $model, $propertyValues); @@ -309,18 +344,18 @@ public function updateModelFromPropertyBag($model, $propertyValues) /** * Return all supported languages from the default data data provider. * - * @param mixed $currentID The id of the item for which to retrieve the valid languages. + * @param mixed $mixID The id of the item for which to retrieve the valid languages. * * @return array */ - public function getSupportedLanguages($currentID) + public function getSupportedLanguages($mixID) { $environment = $this->getEnvironment(); $dataProvider = $environment->getDataProvider(); // Check if current data provider supports multi language. if ($dataProvider instanceof MultiLanguageDataProviderInterface) { - $supportedLanguages = $dataProvider->getLanguages($currentID); + $supportedLanguages = $dataProvider->getLanguages($mixID); } else { $supportedLanguages = null; } @@ -331,6 +366,7 @@ public function getSupportedLanguages($currentID) } $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); // Make an array from the collection. $languages = []; @@ -388,9 +424,14 @@ public function createClonedModel($model) $clone = clone $model; $clone->setId(null); - $environment = $this->getEnvironment(); - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); $dataProvider = $environment->getDataProvider($clone->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); foreach (\array_keys($clone->getPropertiesAsArray()) as $propName) { // If the property is not known, remove it. @@ -412,7 +453,7 @@ public function createClonedModel($model) * * @deprecated Use \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::getModel(). * - * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::getModel(). + * @see ModelCollector::getModel */ public function fetchModelFromProvider($modelId, $providerName = null) { @@ -423,7 +464,12 @@ public function fetchModelFromProvider($modelId, $providerName = null) ); // @codingStandardsIgnoreEnd - return $this->modelCollector->getModel($modelId, $providerName); + $model = $this->modelCollector->getModel($modelId, $providerName); + if (null === $model) { + throw new \RuntimeException('Not found'); + } + + return $model; } /** @@ -431,9 +477,14 @@ public function fetchModelFromProvider($modelId, $providerName = null) */ public function createEmptyModelWithDefaults() { - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); - $dataProvider = $environment->getDataProvider(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $propertyDefinition = $definition->getPropertiesDefinition(); $properties = $propertyDefinition->getProperties(); $model = $dataProvider->getEmptyModel(); @@ -481,7 +532,10 @@ public function getModelsFromClipboardItems(array $items) continue; } - $models->push($environment->getDataProvider($item->getDataProviderName())->getEmptyModel()); + $dataProvider = $environment->getDataProvider($item->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $models->push($dataProvider->getEmptyModel()); } return $models; @@ -492,11 +546,17 @@ public function getModelsFromClipboardItems(array $items) */ public function getModelsFromClipboard(ModelIdInterface $parentModelId = null) { - $environment = $this->getEnvironment(); - $dataDefinition = $environment->getDataDefinition(); + $environment = $this->getEnvironment(); + + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $basicDefinition = $dataDefinition->getBasicDefinition(); $modelProviderName = $basicDefinition->getDataProvider(); - $clipboard = $environment->getClipboard(); + assert(\is_string($modelProviderName)); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); $filter = new Filter(); $filter->andModelIsFromProvider($modelProviderName); @@ -541,67 +601,87 @@ public function applyClipboardActions( */ private function getActionsFromSource(ModelIdInterface $source, ModelIdInterface $parentModelId = null) { - $basicDefinition = $this->getEnvironment()->getDataDefinition()->getBasicDefinition(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + $filter->andModelIsFromProvider($dataProvider); if ($basicDefinition->getParentDataProvider()) { - $filter->andParentIsFromProvider($basicDefinition->getParentDataProvider()); + $parentDataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($parentDataProvider)); + + $filter->andParentIsFromProvider($parentDataProvider); } else { $filter->andHasNoParent(); } $filter->andModelIs($source); - $item = $this->getEnvironment()->getClipboard()->fetch($filter)[0] ?? null; + $clipboard = $this->getEnvironment()->getClipboard(); + assert($clipboard instanceof ClipboardInterface); - $action = $item ? $item->getAction() : ItemInterface::CUT; - $model = $this->modelCollector->getModel($source); - $actions = [ + $item = $clipboard->fetch($filter)[0] ?? null; + + $action = $item ? $item->getAction() : ItemInterface::CUT; + $model = $this->modelCollector->getModel($source); + assert($model instanceof ModelInterface); + + return [ [ 'model' => $model, 'item' => new Item($action, $parentModelId, ModelId::fromModel($model)) ] ]; - - return $actions; } /** * Fetch actions from the clipboard. * - * @param FilterInterface|null $filter The clipboard filter. - * @param ModelIdInterface $parentModelId The parent id. + * @param FilterInterface|null $filter The clipboard filter. + * @param ModelIdInterface|null $parentModelId The parent id. * * @return array */ private function fetchModelsFromClipboard(FilterInterface $filter = null, ModelIdInterface $parentModelId = null) { - $environment = $this->getEnvironment(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); if (!$filter) { $filter = new Filter(); } - $basicDefinition = $dataDefinition->getBasicDefinition(); - $modelProviderName = $basicDefinition->getDataProvider(); - $filter->andModelIsFromProvider($modelProviderName); - if ($parentModelId) { - $filter->andParentIsFromProvider($parentModelId->getDataProviderName()); - } else { - $filter->andHasNoParent(); + if ($filter instanceof Filter) { + $basicDefinition = $dataDefinition->getBasicDefinition(); + $modelProviderName = $basicDefinition->getDataProvider(); + assert(\is_string($modelProviderName)); + $filter->andModelIsFromProvider($modelProviderName); + if ($parentModelId) { + $filter->andParentIsFromProvider($parentModelId->getDataProviderName()); + } else { + $filter->andHasNoParent(); + } } - $environment = $this->getEnvironment(); - $clipboard = $environment->getClipboard(); - $items = $clipboard->fetch($filter); - $actions = []; + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + $items = $clipboard->fetch($filter); + $actions = []; foreach ($items as $item) { $model = null; - if (!$item->isCreate() && $item->getModelId()) { - $model = $this->modelCollector->getModel($item->getModelId()->getId(), $item->getDataProviderName()); + if (!$item->isCreate() && null !== ($model = $item->getModelId())) { + $model = $this->modelCollector->getModel((string) $model->getId(), $item->getDataProviderName()); } $actions[] = [ @@ -616,11 +696,11 @@ private function fetchModelsFromClipboard(FilterInterface $filter = null, ModelI /** * Effectively do the actions. * - * @param array $actions The actions collection. - * @param ModelIdInterface $after The previous model id. - * @param ModelIdInterface $into The hierarchical parent model id. - * @param ModelIdInterface $parentModelId The parent model id. - * @param array $items Write-back clipboard items. + * @param array $actions The action's collection. + * @param ModelIdInterface|null $after The previous model id. + * @param ModelIdInterface|null $into The hierarchical parent model id. + * @param ModelIdInterface|null $parentModelId The parent model id. + * @param array $items Write-back clipboard items. * * @return CollectionInterface */ @@ -637,22 +717,22 @@ private function doActions( $parentModel = null; } - // Holds models, that need deep-copy + // Holds models, that need deep-copy. $deepCopyList = []; - // Apply create and copy actions + // Apply to create and copy actions. foreach ($actions as &$action) { $this->applyAction($action, $deepCopyList, $parentModel); } unset($action); - // When pasting after another model, apply same grouping information + // When pasting after another model, apply same grouping information. $this->ensureSameGrouping($actions, $after); - // Now apply sorting and persist all models + // Now apply sorting and persist all models. $models = $this->sortAndPersistModels($actions, $after, $into, $parentModelId, $items); - // At least, go ahead with the deep copy + // At least, go ahead with the deep copy. $this->doDeepCopy($deepCopyList); return $models; @@ -663,13 +743,12 @@ private function doActions( * * This will create or clone the model in the action. * - * @param array $action The action, containing a model and an item. - * @param array $deepCopyList A list of models that need deep copy. - * @param ModelInterface $parentModel The parent model. + * @param array $action The action, containing a model and an item. + * @param array $deepCopyList A list of models that need deep copy. + * @param ModelInterface|null $parentModel The parent model. * * @return void * - * @throws \UnexpectedValueException When the action is neither create, copy or deep copy. */ private function applyAction(array &$action, array &$deepCopyList, ModelInterface $parentModel = null) { @@ -683,8 +762,10 @@ private function applyAction(array &$action, array &$deepCopyList, ModelInterfac // create new model $model = $this->createEmptyModelWithDefaults(); } elseif ($item->isCopy() || $isDeepCopy = $item->isDeepCopy()) { + assert($model instanceof ModelInterface); // copy model - $model = $this->modelCollector->getModel(ModelId::fromModel($model)); + $model = $this->modelCollector->getModel(ModelId::fromModel($model)); + assert($model instanceof ModelInterface); $clonedModel = $this->doCloneAction($model); if ($isDeepCopy) { @@ -721,16 +802,19 @@ private function doCloneAction(ModelInterface $model) { $environment = $this->getEnvironment(); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + // Make a duplicate. $clonedModel = $this->createClonedModel($model); // Trigger the pre duplicate event. $duplicateEvent = new PreDuplicateModelEvent($environment, $clonedModel, $model); - $environment->getEventDispatcher()->dispatch($duplicateEvent, $duplicateEvent::NAME); + $dispatcher->dispatch($duplicateEvent, $duplicateEvent::NAME); // And trigger the post event for it. $duplicateEvent = new PostDuplicateModelEvent($environment, $clonedModel, $model); - $environment->getEventDispatcher()->dispatch($duplicateEvent, $duplicateEvent::NAME); + $dispatcher->dispatch($duplicateEvent, $duplicateEvent::NAME); return $clonedModel; } @@ -738,8 +822,8 @@ private function doCloneAction(ModelInterface $model) /** * Ensure all models have the same grouping. * - * @param array $actions The actions collection. - * @param ModelIdInterface $after The previous model id. + * @param array $actions The action's collection. + * @param ModelIdInterface|null $after The previous model id. * * @return void */ @@ -751,6 +835,8 @@ private function ensureSameGrouping(array $actions, ModelIdInterface $after = nu // when pasting after another item, inherit the grouping field $groupingField = $groupingMode['property']; $previous = $this->modelCollector->getModel($after); + assert($previous instanceof ModelInterface); + $groupingValue = $previous->getProperty($groupingField); foreach ($actions as $action) { @@ -764,15 +850,14 @@ private function ensureSameGrouping(array $actions, ModelIdInterface $after = nu /** * Apply sorting and persist all models. * - * @param array $actions The actions collection. - * @param ModelIdInterface $after The previous model id. - * @param ModelIdInterface $into The hierarchical parent model id. - * @param ModelIdInterface $parentModelId The parent model id. - * @param array $items Write-back clipboard items. + * @param array $actions The actions collection. + * @param ModelIdInterface|null $after The previous model id. + * @param ModelIdInterface|null $into The hierarchical parent model id. + * @param ModelIdInterface|null $parentModelId The parent model id. + * @param array $items Write-back clipboard items. * - * @return DefaultCollection|ModelInterface[] + * @return DefaultCollection * - * @throws DcGeneralRuntimeException When the parameters for the pasting destination are invalid. */ private function sortAndPersistModels( array $actions, @@ -781,8 +866,8 @@ private function sortAndPersistModels( ModelIdInterface $parentModelId = null, array &$items = [] ) { - /** @var DefaultCollection|ModelInterface[] $models */ $models = $this->createModelCollectionFromActions($actions, $items); + assert($models instanceof CollectionInterface); $this->triggerPrePasteModel($models); @@ -801,8 +886,8 @@ private function sortAndPersistModels( /** * Process paste the collection of models after the a model. * - * @param CollectionInterface $models The collection of models. - * @param ModelIdInterface $after The paste after model. + * @param CollectionInterface $models The collection of models. + * @param ModelIdInterface|null $after The paste after model. * * @return void */ @@ -810,8 +895,12 @@ private function processPasteAfter(CollectionInterface $models, ModelIdInterface { if ($after && $models->count() && $after->getId()) { $manualSorting = ViewHelpers::getManualSortingProperty($this->getEnvironment()); + assert(\is_string($manualSorting)); + + $model = $this->modelCollector->getModel($after); + assert($model instanceof ModelInterface); - $this->pasteAfter($this->modelCollector->getModel($after), $models, $manualSorting); + $this->pasteAfter($model, $models, $manualSorting); $this->triggerPostPasteModel($models); $this->clearModelCollection($models); @@ -819,10 +908,10 @@ private function processPasteAfter(CollectionInterface $models, ModelIdInterface } /** - * Process paste the collection of models into the a model. + * Process paste the collection of models into the model. * - * @param CollectionInterface $models The collection of models. - * @param ModelIdInterface $into The paste into model. + * @param CollectionInterface $models The collection of models. + * @param ModelIdInterface|null $into The paste into model. * * @return void */ @@ -830,8 +919,12 @@ private function processPasteInto(CollectionInterface $models, ModelIdInterface { if ($into && $models->count() && $into->getId()) { $manualSorting = ViewHelpers::getManualSortingProperty($this->getEnvironment()); + assert(\is_string($manualSorting)); + + $model = $this->modelCollector->getModel($into); + assert($model instanceof ModelInterface); - $this->pasteInto($this->modelCollector->getModel($into), $models, $manualSorting); + $this->pasteInto($model, $models, $manualSorting); $this->triggerPostPasteModel($models); $this->clearModelCollection($models); @@ -841,10 +934,10 @@ private function processPasteInto(CollectionInterface $models, ModelIdInterface /** * Process paste the content of the clipboard onto the top after a model without reference. * - * @param CollectionInterface $models The collection of models. - * @param ModelIdInterface $after The previous model id. - * @param ModelIdInterface $into The hierarchical parent model id. - * @param ModelIdInterface $parent The parent model id. + * @param CollectionInterface $models The collection of models. + * @param ModelIdInterface|null $after The previous model id. + * @param ModelIdInterface|null $into The hierarchical parent model id. + * @param ModelIdInterface|null $parent The parent model id. * * @return void */ @@ -859,8 +952,11 @@ private function processPasteTopWithoutReference( && (($after && (0 === (int) $after->getId())) || ($into && (0 === (int) $into->getId()))) ) { - $manualSorting = ViewHelpers::getManualSortingProperty($this->getEnvironment()); + $manualSorting = ViewHelpers::getManualSortingProperty($this->getEnvironment()); + assert(\is_string($manualSorting)); + $dataDefinition = $this->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); if (BasicDefinitionInterface::MODE_HIERARCHICAL === $dataDefinition->getBasicDefinition()->getMode()) { $this->relationshipManager->setAllRoot($models); @@ -893,6 +989,8 @@ private function processPasteTopAfterModel(CollectionInterface $models, ModelIdI } $dataProvider = $this->getEnvironment()->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $dataProvider->saveEach($models); $this->triggerPostPasteModel($models); @@ -930,7 +1028,11 @@ private function triggerPrePasteModel(CollectionInterface $collection) { foreach ($collection as $model) { $event = new PrePasteModelEvent($this->getEnvironment(), $model); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, $event::NAME); + + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); } } @@ -945,7 +1047,11 @@ private function triggerPostPasteModel(CollectionInterface $collection) { foreach ($collection as $model) { $event = new PostPasteModelEvent($this->getEnvironment(), $model); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, $event::NAME); + + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); } } @@ -964,8 +1070,11 @@ protected function doDeepCopy(array $deepCopyList) return; } - $factory = DcGeneralFactory::deriveFromEnvironment($this->getEnvironment()); - $dataDefinition = $this->getEnvironment()->getDataDefinition(); + $factory = DcGeneralFactory::deriveFromEnvironment($this->getEnvironment()); + + $dataDefinition = $this->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $modelRelationshipDefinition = $dataDefinition->getModelRelationshipDefinition(); $childConditions = $modelRelationshipDefinition->getChildConditions($dataDefinition->getName()); @@ -981,13 +1090,22 @@ protected function doDeepCopy(array $deepCopyList) // create new destination environment $destinationName = $childCondition->getDestinationName(); $factory->setContainerName($destinationName); - $destinationEnvironment = $factory->createEnvironment(); + $destinationEnvironment = $factory->createEnvironment(); + $destinationDataDefinition = $destinationEnvironment->getDataDefinition(); + assert($destinationDataDefinition instanceof ContainerInterface); + $destinationViewDefinition = $destinationDataDefinition->getDefinition( Contao2BackendViewDefinitionInterface::NAME ); - $destinationDataProvider = $destinationEnvironment->getDataProvider(); - $destinationController = $destinationEnvironment->getController(); + assert($destinationViewDefinition instanceof Contao2BackendViewDefinitionInterface); + + $destinationDataProvider = $destinationEnvironment->getDataProvider(); + assert($destinationDataProvider instanceof DataProviderInterface); + + $destinationController = $destinationEnvironment->getController(); + assert($destinationController instanceof ControllerInterface); + /** @var Contao2BackendViewDefinitionInterface $destinationViewDefinition */ /** @var DefaultController $destinationController */ $listingConfig = $destinationViewDefinition->getListingConfig(); @@ -1017,6 +1135,10 @@ protected function doDeepCopy(array $deepCopyList) // build the copy actions foreach ($children as $childModel) { + if (!($childModel instanceof ModelInterface)) { + continue; + } + $childModelId = ModelId::fromModel($childModel); $actions[] = [ @@ -1044,11 +1166,18 @@ public function pasteTop(CollectionInterface $models, $sortedBy, ModelIdInterfac { $environment = $this->getEnvironment(); + $firstModel = $models->get(0); + assert($firstModel instanceof ModelInterface); + // Enforce proper sorting now. - $siblings = $this->modelCollector->collectSiblingsOf($models->get(0), $sortedBy, $parentId); + $siblings = $this->modelCollector->collectSiblingsOf($firstModel, $sortedBy, $parentId); $newList = (new SortingManager($models, $siblings, $sortedBy, null))->getResults(); + assert($newList instanceof CollectionInterface); + + $dataProvider = $environment->getDataProvider($firstModel->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); - $environment->getDataProvider($models->get(0)->getProviderName())->saveEach($newList); + $dataProvider->saveEach($newList); } /** @@ -1061,11 +1190,15 @@ public function pasteAfter(ModelInterface $previousModel, CollectionInterface $m if (0 === $models->length()) { throw new \RuntimeException('No models passed to pasteAfter().'); } + $environment = $this->getEnvironment(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + if ( \in_array( - $environment->getDataDefinition()->getBasicDefinition()->getMode(), + $definition->getBasicDefinition()->getMode(), [ BasicDefinitionInterface::MODE_HIERARCHICAL, BasicDefinitionInterface::MODE_PARENTEDLIST @@ -1074,6 +1207,8 @@ public function pasteAfter(ModelInterface $previousModel, CollectionInterface $m ) { if (!$this->relationshipManager->isRoot($previousModel)) { $parentModel = $this->modelCollector->searchParentOf($previousModel); + assert($parentModel instanceof ModelInterface); + $parentName = $parentModel->getProviderName(); $this->relationshipManager->setSameParentForAll($models, $previousModel, $parentName); } else { @@ -1084,8 +1219,12 @@ public function pasteAfter(ModelInterface $previousModel, CollectionInterface $m // Enforce proper sorting now. $siblings = $this->modelCollector->collectSiblingsOf($previousModel, $sortedBy); $newList = (new SortingManager($models, $siblings, $sortedBy, $previousModel))->getResults(); + assert($newList instanceof CollectionInterface); + + $dataProvider = $environment->getDataProvider($previousModel->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); - $environment->getDataProvider($previousModel->getProviderName())->saveEach($newList); + $dataProvider->saveEach($newList); } /** @@ -1100,8 +1239,15 @@ public function pasteInto(ModelInterface $parentModel, CollectionInterface $mode // Enforce proper sorting now. $siblings = $this->assembleChildrenFor($parentModel, $sortedBy); $newList = (new SortingManager($models, $siblings, $sortedBy))->getResults(); + assert($newList instanceof CollectionInterface); + + $firstItem = $newList->get(0); + assert($firstItem instanceof ModelInterface); + + $dataProvider = $environment->getDataProvider($firstItem->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); - $environment->getDataProvider($newList->get(0)->getProviderName())->saveEach($newList); + $dataProvider->saveEach($newList); } /** diff --git a/src/Controller/ModelCollector.php b/src/Controller/ModelCollector.php index 5babbb761..99c4b25ad 100644 --- a/src/Controller/ModelCollector.php +++ b/src/Controller/ModelCollector.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,13 +16,14 @@ * @author Ingolf Steinhardt * @author David Molineus * @author Stefan Heimes - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Controller; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; @@ -30,6 +31,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\ModelRelationshipDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\RootConditionInterface; @@ -55,7 +57,7 @@ class ModelCollector /** * The mode the definition is in. * - * @var int + * @var int|null */ private $definitionMode; @@ -76,35 +78,35 @@ class ModelCollector /** * The root data provider. * - * @var DataProviderInterface + * @var DataProviderInterface|null */ private $rootProvider; /** * The root data provider name. * - * @var string + * @var string|null */ private $rootProviderName; /** * The parent data provider. * - * @var DataProviderInterface + * @var DataProviderInterface|null */ private $parentProvider; /** * The parent data provider name. * - * @var string + * @var string|null */ private $parentProviderName; /** * The default data provider name. * - * @var string + * @var string|null */ private $defaultProviderName; @@ -117,9 +119,14 @@ class ModelCollector */ public function __construct(EnvironmentInterface $environment) { - $this->environment = $environment; - $definition = $this->environment->getDataDefinition(); - $basicDefinition = $definition->getBasicDefinition(); + $this->environment = $environment; + + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + $this->definitionMode = $basicDefinition->getMode(); $this->relationships = $definition->getModelRelationshipDefinition(); $this->defaultProviderName = $basicDefinition->getDataProvider(); @@ -147,20 +154,20 @@ public function __construct(EnvironmentInterface $environment) /** * Fetch a certain model from its provider. * - * @param string|ModelIdInterface $modelId This is either the id of the model or a serialized id. + * @param ModelIdInterface|string $modelId This is either the id of the model or a serialized id. * @param string|null $providerName The name of the provider, if this is empty, the id will be * deserialized and the provider name will get extracted from there. * - * @return ModelInterface + * @return ModelInterface|null * * @throws \InvalidArgumentException When the model id is invalid. */ public function getModel($modelId, $providerName = null) { if (\is_string($modelId)) { - try { + if (null !== $providerName) { $modelId = ModelId::fromValues($providerName, $modelId); - } catch (\Exception $swallow) { + } else { $modelId = ModelId::fromSerialized($modelId); } } @@ -169,14 +176,19 @@ public function getModel($modelId, $providerName = null) throw new \InvalidArgumentException('Invalid model id passed: ' . \var_export($modelId, true)); } - $definition = $this->environment->getDataDefinition(); + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $parentDefinition = $this->environment->getParentDataDefinition(); - $dataProvider = $this->environment->getDataProvider($modelId->getDataProviderName()); - $config = $dataProvider->getEmptyConfig(); + + $dataProvider = $this->environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $config = $dataProvider->getEmptyConfig(); if ($definition->getName() === $modelId->getDataProviderName()) { $propertyDefinition = $definition->getPropertiesDefinition(); - } elseif ($parentDefinition->getName() === $modelId->getDataProviderName()) { + } elseif ($parentDefinition && $parentDefinition->getName() === $modelId->getDataProviderName()) { $propertyDefinition = $parentDefinition->getPropertiesDefinition(); } else { throw new \InvalidArgumentException('Invalid provider name ' . $modelId->getDataProviderName()); @@ -228,8 +240,12 @@ public function searchParentOfIn(ModelInterface $model, CollectionInterface $mod } $provider = $this->environment->getDataProvider($condition->getDestinationName()); - $config = $provider->getEmptyConfig()->setFilter($condition->getFilter($candidate)); - $result = $this->searchParentOfIn($model, $provider->fetchAll($config)); + assert($provider instanceof DataProviderInterface); + + $config = $provider->getEmptyConfig()->setFilter($condition->getFilter($candidate)); + $parentCollection = $provider->fetchAll($config); + assert($parentCollection instanceof CollectionInterface); + $result = $this->searchParentOfIn($model, $parentCollection); if (null !== $result) { return $result; } @@ -289,12 +305,18 @@ public function searchParentFromHierarchical(ModelInterface $model): ?ModelInter if ($this->rootProviderName !== $model->getProviderName()) { throw new DcGeneralInvalidArgumentException( 'Model originates from ' . $model->getProviderName() . - ' but is expected to be from ' . $this->rootProviderName . + ' but is expected to be from ' . ($this->rootProviderName ?? '') . ' can not determine parent.' ); } - $condition = $this->relationships->getChildCondition($this->parentProviderName, $this->rootProviderName); + $parentProviderName = $this->parentProviderName; + assert(\is_string($parentProviderName)); + + $rootProviderName = $this->rootProviderName; + assert(\is_string($rootProviderName)); + + $condition = $this->relationships->getChildCondition($parentProviderName, $rootProviderName); if (null === $condition) { throw new DcGeneralInvalidArgumentException( 'Invalid configuration. Child condition must be defined!' @@ -304,7 +326,9 @@ public function searchParentFromHierarchical(ModelInterface $model): ?ModelInter return $this->parentProvider->fetch($this->parentProvider->getEmptyConfig()->setFilter($inverseFilter)); } - foreach ($this->parentProvider->fetchAll($this->parentProvider->getEmptyConfig()) as $candidate) { + $parentCollection = $this->parentProvider->fetchAll($this->parentProvider->getEmptyConfig()); + assert($parentCollection instanceof CollectionInterface); + foreach ($parentCollection as $candidate) { if ($condition->matches($candidate, $model)) { return $candidate; } @@ -316,39 +340,46 @@ public function searchParentFromHierarchical(ModelInterface $model): ?ModelInter /** * Retrieve all siblings of a given model. * - * @param ModelInterface $model The model for which the siblings shall be retrieved from. - * @param string|null $sortingProperty The property name to use for sorting. - * @param ModelIdInterface $parentId The (optional) parent id to use. + * @param ModelInterface $model The model for which the siblings shall be retrieved from. + * @param string|null $sortingProperty The property name to use for sorting. + * @param ModelIdInterface|null $parentId The (optional) parent id to use. * * @return CollectionInterface * - * @throws DcGeneralRuntimeException When no parent model can be located. */ public function collectSiblingsOf( ModelInterface $model, $sortingProperty = null, ModelIdInterface $parentId = null ) { - $config = $this->environment->getBaseConfigRegistry()->getBaseConfig($parentId); + $registry = $this->environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $config = $registry->getBaseConfig($parentId); // Add the parent filter. $this->addParentFilter($model, $config); if (null !== $sortingProperty) { - $config->setSorting([(string) $sortingProperty => 'ASC']); + $config->setSorting([$sortingProperty => 'ASC']); } // Handle grouping. $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $viewDefinition */ $viewDefinition = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); - if ($viewDefinition && ($viewDefinition instanceof Contao2BackendViewDefinitionInterface)) { + if ($viewDefinition instanceof Contao2BackendViewDefinitionInterface) { $listingConfig = $viewDefinition->getListingConfig(); - $sortingProperties = \array_keys((array) $listingConfig->getDefaultSortingFields()); + /** @psalm-suppress DeprecatedMethod */ + $sortingProperties = \array_keys($listingConfig->getDefaultSortingFields()); $sortingPropertyIndex = \array_search($sortingProperty, $sortingProperties); if (false !== $sortingPropertyIndex && $sortingPropertyIndex > 0) { $sortingProperties = \array_slice($sortingProperties, 0, $sortingPropertyIndex); - $filters = $config->getFilter(); + + $filters = $config->getFilter(); + assert(\is_array($filters)); foreach ($sortingProperties as $propertyName) { $filters[] = [ @@ -362,7 +393,12 @@ public function collectSiblingsOf( } } - return $this->environment->getDataProvider($model->getProviderName())->fetchAll($config); + $dataProvider = $this->environment->getDataProvider($model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $siblingCollection = $dataProvider->fetchAll($config); + assert($siblingCollection instanceof CollectionInterface); + return $siblingCollection; } /** @@ -406,12 +442,15 @@ public function collectDirectChildrenOf(ModelInterface $model, $providerName = ' * @param string $providerName The name of the data provider to fetch children from. * @param bool $recursive Determine for recursive sampling. For models with included child models. * - * @return array + * @return list * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - private function internalCollectChildrenOf(ModelInterface $model, $providerName = '', $recursive = false) - { + private function internalCollectChildrenOf( + ModelInterface $model, + string $providerName = '', + bool $recursive = false + ) { if ('' === $providerName) { $providerName = $model->getProviderName(); } @@ -422,10 +461,13 @@ private function internalCollectChildrenOf(ModelInterface $model, $providerName $childIds = []; foreach ($this->relationships->getChildConditions($model->getProviderName()) as $condition) { $provider = $this->environment->getDataProvider($condition->getDestinationName()); - $config = $provider->getEmptyConfig(); + assert($provider instanceof DataProviderInterface); + + $config = $provider->getEmptyConfig(); $config->setFilter($condition->getFilter($model)); $result = $provider->fetchAll($config); + assert($result instanceof CollectionInterface); if (!$recursive && $result->length() === 0) { return []; } @@ -444,7 +486,7 @@ private function internalCollectChildrenOf(ModelInterface $model, $providerName } } - return \array_merge($ids, ...$childIds); + return \array_values(\array_merge($ids, ...$childIds)); } /** @@ -460,7 +502,13 @@ private function searchParentOfInParentedMode(ModelInterface $model) { $this->guardParentProviderDefined(); - $condition = $this->relationships->getChildCondition($this->parentProviderName, $this->defaultProviderName); + $defaultProviderName = $this->defaultProviderName; + assert(\is_string($defaultProviderName)); + + $parentProviderName = $this->parentProviderName; + assert(\is_string($parentProviderName)); + + $condition = $this->relationships->getChildCondition($parentProviderName, $defaultProviderName); if (null === $condition) { throw new DcGeneralInvalidArgumentException( @@ -468,11 +516,15 @@ private function searchParentOfInParentedMode(ModelInterface $model) ); } + $parentProvider = $this->parentProvider; + assert($parentProvider instanceof DataProviderInterface); + if (null !== ($inverseFilter = $condition->getInverseFilterFor($model))) { - return $this->parentProvider->fetch($this->parentProvider->getEmptyConfig()->setFilter($inverseFilter)); + return $parentProvider->fetch($parentProvider->getEmptyConfig()->setFilter($inverseFilter)); } - - foreach ($this->parentProvider->fetchAll($this->parentProvider->getEmptyConfig()) as $candidate) { + $parentCollection = $parentProvider->fetchAll($parentProvider->getEmptyConfig()); + assert($parentCollection instanceof CollectionInterface); + foreach ($parentCollection as $candidate) { if ($condition->matches($candidate, $model)) { return $candidate; } @@ -508,8 +560,10 @@ private function searchParentOfInHierarchical(ModelInterface $model) } $provider = $this->environment->getDataProvider($condition->getSourceName()); + assert($provider instanceof DataProviderInterface); + $config = $provider->getEmptyConfig()->setFilter($inverseFilter); - $parent = $this->environment->getDataProvider($condition->getSourceName())->fetch($config); + $parent = $provider->fetch($config); if (null !== $parent) { return $parent; @@ -517,9 +571,14 @@ private function searchParentOfInHierarchical(ModelInterface $model) } // Start from the root data provider and walk through the whole tree. // To speed up, some conditions have an inverse filter - we should use them! - $config = $this->rootProvider->getEmptyConfig()->setFilter($this->rootCondition->getFilterArray()); - - return $this->searchParentOfIn($model, $this->rootProvider->fetchAll($config)); + $rootProvider = $this->rootProvider; + assert($rootProvider instanceof DataProviderInterface); + $rootCondition = $this->rootCondition; + assert($rootCondition instanceof RootConditionInterface); + $config = $rootProvider->getEmptyConfig()->setFilter($rootCondition->getFilterArray()); + $parentCollection = $rootProvider->fetchAll($config); + assert($parentCollection instanceof CollectionInterface); + return $this->searchParentOfIn($model, $parentCollection); } /** @@ -542,7 +601,10 @@ private function addParentFilter(ModelInterface $model, $config) // Root model? if ($this->isRootModel($model)) { - $config->setFilter($this->rootCondition->getFilterArray()); + $rootCondition = $this->rootCondition; + assert($rootCondition instanceof RootConditionInterface); + + $config->setFilter($rootCondition->getFilterArray()); return; } @@ -626,7 +688,7 @@ private function guardModelOriginatesFromProvider(ModelInterface $model): void throw new DcGeneralInvalidArgumentException( 'Model originates from ' . $model->getProviderName() . - ' but is expected to be from ' . $this->defaultProviderName . + ' but is expected to be from ' . ($this->defaultProviderName ?? '') . ' can not determine parent.' ); } diff --git a/src/Controller/SortingManager.php b/src/Controller/SortingManager.php index f65ec70b0..8763b3c0d 100644 --- a/src/Controller/SortingManager.php +++ b/src/Controller/SortingManager.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,35 +32,35 @@ class SortingManager /** * The collection containing the models to be inserted. * - * @var CollectionInterface + * @var CollectionInterface|null */ protected $models; /** * The collection containing the models that are siblings. * - * @var CollectionInterface + * @var CollectionInterface|null */ - protected $siblings; + protected $siblings = null; /** * The collection containing the models that are siblings (working copy). * - * @var CollectionInterface + * @var CollectionInterface|null */ protected $siblingsCopy; /** * The result collection. * - * @var CollectionInterface + * @var CollectionInterface|null */ protected $results; /** * The model preceding the target position of the first model from the collection. * - * @var null|ModelInterface + * @var ModelInterface|null */ protected $previousModel; @@ -69,12 +69,12 @@ class SortingManager * * @var string */ - protected $sortingProperty; + protected $sortingProperty = ''; /** * Temporary marker containing the model currently in scope. * - * @var null|ModelInterface + * @var ModelInterface|null */ protected $marker; @@ -83,21 +83,21 @@ class SortingManager * * @var int */ - protected $position; + protected $position = 0; /** * Create a new instance. * - * @param CollectionInterface $models The collection containing the models to be inserted. - * @param CollectionInterface $siblings The collection containing the models that are siblings. - * @param string $sortedBy The property that is used for sorting. - * @param ModelInterface $previousModel The model preceding the target position of the first model from the - * collection. + * @param CollectionInterface|null $models The collection containing the models to be inserted. + * @param CollectionInterface|null $siblings The collection containing the models that are siblings. + * @param string|null $sortedBy The property that is used for sorting. + * @param ModelInterface|null $previousModel The model preceding the target position of the first model from + * the collection. */ public function __construct( CollectionInterface $models = null, CollectionInterface $siblings = null, - $sortedBy = null, + string $sortedBy = null, ModelInterface $previousModel = null ) { if ($models) { @@ -135,7 +135,7 @@ public function setModels(CollectionInterface $models) /** * Get the collection containing the models to be inserted. * - * @return CollectionInterface + * @return CollectionInterface|null */ public function getModels() { @@ -171,7 +171,7 @@ public function getPreviousModel() /** * Get the result collection. * - * @return CollectionInterface + * @return CollectionInterface|null */ public function getResults() { @@ -227,8 +227,11 @@ public function getSortingProperty() */ protected function getModelIds() { - $ids = []; + if (null === $this->models) { + return []; + } + $ids = []; foreach ($this->models as $model) { /** @var ModelInterface $model */ $ids[] = $model->getId(); @@ -248,32 +251,37 @@ protected function scanToDesiredPosition() $this->marker = null; $this->position = 0; $ids = $this->getModelIds(); + + $siblingsCopy = $this->siblingsCopy; + assert($siblingsCopy instanceof CollectionInterface); + // If no previous model, insert at beginning. if (null === $this->previousModel) { - if ($this->siblingsCopy->length()) { - $this->marker = $this->siblingsCopy->shift(); + if ($siblingsCopy->length()) { + $this->marker = $siblingsCopy->shift(); } return; } - if ($this->siblingsCopy->length()) { + $previousModel = $this->getPreviousModel(); + assert($previousModel instanceof ModelInterface); + + if ($siblingsCopy->length()) { // Search for "previous" sibling. do { - $this->marker = $this->siblingsCopy->shift(); - - if (\in_array($this->marker->getId(), $ids)) { - continue; - } - + $this->marker = $siblingsCopy->shift(); if ($this->marker) { + if (\in_array($this->marker->getId(), $ids, true)) { + continue; + } $this->position = $this->marker->getProperty($this->getSortingProperty()); } - } while ($this->marker && $this->marker->getId() !== $this->getPreviousModel()->getId()); + } while ($this->marker && $this->marker->getId() !== $previousModel->getId()); // Remember the "next" sibling. if ($this->marker) { - $this->marker = $this->siblingsCopy->shift(); + $this->marker = $siblingsCopy->shift(); } } } @@ -287,14 +295,20 @@ protected function scanToDesiredPosition() */ private function determineDelta() { + $marker = $this->marker; + assert($marker instanceof ModelInterface); + + $results = $this->results; + assert($results instanceof CollectionInterface); + $delta = ( - ($this->marker->getProperty($this->getSortingProperty()) - $this->position) / $this->results->length() + ($marker->getProperty($this->getSortingProperty()) - $this->position) / $results->length() ); // If delta too narrow, we need to make room. // Prevent delta to exceed, also. Use minimum delta which is calculated as multiple of 128. if (($delta < 2) || ($delta > 128)) { - return (\ceil($this->results->length() / 128) * 128); + return (\ceil($results->length() / 128) * 128); } return $delta; @@ -307,6 +321,8 @@ private function determineDelta() */ private function updateSorting() { + // Called from calculate() only when siblings exist. + assert($this->results instanceof CollectionInterface); $ids = $this->getModelIds(); // If no "next" sibling, simply increment the sorting as we are at the end of the list. if (!$this->marker) { @@ -323,7 +339,7 @@ private function updateSorting() // Loop over all models and increment sorting value. foreach ($this->results as $model) { - $this->position += $delta; + $this->position += (int) $delta; /** @var ModelInterface $model */ $model->setProperty($this->getSortingProperty(), $this->position); } @@ -332,17 +348,20 @@ private function updateSorting() // end of the list. if ($this->marker->getProperty($this->getSortingProperty()) <= $this->position) { do { + $siblingsCopy = $this->siblingsCopy; + assert($siblingsCopy instanceof CollectionInterface); + // Skip models about to be pasted. if (\in_array($this->marker->getId(), $ids)) { - $this->marker = $this->siblingsCopy->shift(); + $this->marker = $siblingsCopy->shift(); continue; } - $this->position += $delta; + $this->position += (int) $delta; $this->marker->setProperty($this->getSortingProperty(), $this->position); $this->results->push($this->marker); - $this->marker = $this->siblingsCopy->shift(); + $this->marker = $siblingsCopy->shift(); } while ($this->marker); } } @@ -356,16 +375,22 @@ private function updateSorting() */ protected function calculate() { - if (isset($this->results) || (0 === $this->models->length())) { + $models = $this->models; + assert($models instanceof CollectionInterface); + + if (isset($this->results) || (0 === $models->length())) { return; } if (!$this->getSortingProperty()) { - throw new \RuntimeException('No sorting property defined for ' . $this->models->get(0)->getProviderName()); + $firstModel = $models->get(0); + assert($firstModel instanceof ModelInterface); + + throw new \RuntimeException('No sorting property defined for ' . $firstModel->getProviderName()); } - $this->results = clone $this->models; - $this->siblingsCopy = clone $this->siblings; + $this->results = clone $models; + $this->siblingsCopy = $this->siblings ? clone $this->siblings : null; $this->scanToDesiredPosition(); $this->updateSorting(); diff --git a/src/Controller/TreeCollector.php b/src/Controller/TreeCollector.php index c0b34537f..434026dba 100644 --- a/src/Controller/TreeCollector.php +++ b/src/Controller/TreeCollector.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,18 +14,22 @@ * @author Christian Schiffler * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Controller; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\DCGE; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\ModelRelationshipDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\ParentChildConditionInterface; @@ -35,6 +39,8 @@ /** * Generic class to retrieve a tree collection for tree views. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class TreeCollector implements EnvironmentAwareInterface { @@ -82,7 +88,7 @@ public function __construct( ) { $this->environment = $environment; $this->panel = $panel; - $this->sorting = (array) $sorting; + $this->sorting = $sorting; $this->states = $states; } @@ -144,7 +150,10 @@ private function determineModelState(ModelInterface $model, $level) private function getChildProvidersOf($parentProvider, $relationships = null) { if (null === $relationships) { - $relationships = $this->getEnvironment()->getDataDefinition()->getModelRelationshipDefinition(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); } $mySubTables = []; @@ -164,8 +173,11 @@ private function getChildProvidersOf($parentProvider, $relationships = null) * * @return CollectionInterface|null */ - private function getChildrenOfModel($dataProvider, $model, $childCondition) - { + private function getChildrenOfModel( + DataProviderInterface $dataProvider, + ModelInterface $model, + ParentChildConditionInterface $childCondition + ): ?CollectionInterface { $childIds = $dataProvider ->fetchAll( $dataProvider @@ -173,15 +185,13 @@ private function getChildrenOfModel($dataProvider, $model, $childCondition) ->setFilter($childCondition->getFilter($model)) ->setIdOnly(true) ); + assert(\is_array($childIds)); - if ( - ($childIds instanceof CollectionInterface && !$childIds->count()) - || (\is_array($childIds) && !\count($childIds)) - ) { + if (!\count($childIds)) { return null; } - return $dataProvider->fetchAll( + $children = $dataProvider->fetchAll( $dataProvider ->getEmptyConfig() ->setSorting(['sorting' => 'ASC']) @@ -192,6 +202,10 @@ private function getChildrenOfModel($dataProvider, $model, $childCondition) ->getAllAsArray() ) ); + + assert($children instanceof CollectionInterface); + + return $children; } /** @@ -203,10 +217,14 @@ private function getChildrenOfModel($dataProvider, $model, $childCondition) * * @return void */ - private function treeWalkModel(ModelInterface $model, $intLevel, $subTables = []) + private function treeWalkModel(ModelInterface $model, int $intLevel, array $subTables = []): void { - $environment = $this->getEnvironment(); - $relationships = $environment->getDataDefinition()->getModelRelationshipDefinition(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); $hasChildren = false; $this->determineModelState($model, $intLevel); @@ -223,18 +241,19 @@ private function treeWalkModel(ModelInterface $model, $intLevel, $subTables = [] continue; } - $dataProvider = $environment->getDataProvider($subTable); - $childCollection = $this->getChildrenOfModel($dataProvider, $model, $childFilter); - $hasChildren = (bool) $childCollection; + $dataProvider = $environment->getDataProvider($subTable); + assert($dataProvider instanceof DataProviderInterface); - // Speed up - we may exit if we have at least one child but the parenting model is collapsed. - if ($hasChildren && !$model->getMeta($model::SHOW_CHILDREN)) { - break; - } + $childCollection = $this->getChildrenOfModel($dataProvider, $model, $childFilter); + $hasChildren = null !== $childCollection; if ($hasChildren) { + // Speed up - we may exit if we have at least one child but the parenting model is collapsed. + if (!$model->getMeta($model::SHOW_CHILDREN)) { + break; + } foreach ($childCollection as $childModel) { - // Let the child know about it's parent. + // Let the child know about its parent. $model->setMeta(ModelInterface::PARENT_ID, $model->getID()); $model->setMeta(ModelInterface::PARENT_PROVIDER_NAME, $providerName); @@ -264,29 +283,35 @@ private function treeWalkModel(ModelInterface $model, $intLevel, $subTables = [] */ private function addParentFilter(ConfigInterface $config, ModelInterface $parentModel) { - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); - if (!$basicDefinition->getParentDataProvider()) { - return; - } + $parentDataProvider = $basicDefinition->getParentDataProvider(); + assert(\is_string($parentDataProvider)); - if ($basicDefinition->getParentDataProvider() !== $parentModel->getProviderName()) { + if ($parentDataProvider !== $parentModel->getProviderName()) { throw new \RuntimeException( \sprintf( 'Parent provider mismatch: %s vs. %s', - $basicDefinition->getParentDataProvider(), + $parentDataProvider, $parentModel->getProviderName() ) ); } + $rootDataProvider = $basicDefinition->getRootDataProvider(); + assert(\is_string($rootDataProvider)); + // Apply parent filtering, do this only for root elements. if ( $parentCondition = $definition->getModelRelationshipDefinition()->getChildCondition( - $basicDefinition->getParentDataProvider(), - $basicDefinition->getRootDataProvider() + $parentDataProvider, + $rootDataProvider ) ) { $baseFilter = $config->getFilter(); @@ -307,9 +332,10 @@ private function addParentFilter(ConfigInterface $config, ModelInterface $parent */ private function calculateRootConfig() { - $rootConfig = $this - ->getEnvironment() - ->getBaseConfigRegistry() + $registry = $this->getEnvironment()->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $rootConfig = $registry ->getBaseConfig() ->setSorting($this->getSorting()); $this->getPanel()->initialize($rootConfig); @@ -330,9 +356,11 @@ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = { $environment = $this->getEnvironment(); $dataProvider = $environment->getDataProvider($providerName); + assert($dataProvider instanceof DataProviderInterface); // Fetch root element. $rootModel = $dataProvider->fetch($this->calculateRootConfig()->setId($rootId)); + assert($rootModel instanceof ModelInterface); $this->treeWalkModel($rootModel, $level, $this->getChildProvidersOf($rootModel->getProviderName())); $rootCollection = $dataProvider->getEmptyCollection(); @@ -352,11 +380,17 @@ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = */ public function getChildrenOf($providerName, $level = 0, $parentModel = null) { - $environment = $this->getEnvironment(); + $environment = $this->getEnvironment(); + $dataProvider = $environment->getDataProvider($providerName); - $rootConfig = $this->calculateRootConfig(); + assert($dataProvider instanceof DataProviderInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $rootConfig = $this->calculateRootConfig(); - $rootCondition = $environment->getDataDefinition()->getModelRelationshipDefinition()->getRootCondition(); + $rootCondition = $definition->getModelRelationshipDefinition()->getRootCondition(); if (null !== $rootCondition) { $baseFilter = $rootConfig->getFilter(); $filter = $rootCondition->getFilterArray(); @@ -374,6 +408,7 @@ public function getChildrenOf($providerName, $level = 0, $parentModel = null) $rootCollection = $dataProvider->fetchAll($rootConfig); $tableTreeData = $dataProvider->getEmptyCollection(); + assert($rootCollection instanceof CollectionInterface); if ($rootCollection->length() > 0) { $mySubTables = $this->getChildProvidersOf($providerName); diff --git a/src/Controller/TreeNodeStates.php b/src/Controller/TreeNodeStates.php index 9eadadfd7..04501d2a3 100644 --- a/src/Controller/TreeNodeStates.php +++ b/src/Controller/TreeNodeStates.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,14 +33,14 @@ class TreeNodeStates * * @var array */ - private $states; + private $states = []; /** * List of implicit open nodes (selected values i.e.). * * @var array */ - private $implicitOpen; + private $implicitOpen = []; /** * Create a new instance. @@ -67,7 +68,7 @@ public function __construct($states = [], $implicitOpen = []) */ public function setStates($states) { - $this->states = (array) $states; + $this->states = $states; return $this; } @@ -96,7 +97,7 @@ public function getStates() */ public function setImplicitOpen($implicitOpen) { - $this->implicitOpen = (array) $implicitOpen; + $this->implicitOpen = $implicitOpen; return $this; } @@ -136,7 +137,7 @@ public function isAllOpen() */ public function setAllOpen($allOpen) { - $this->states['all'] = (bool) $allOpen; + $this->states['all'] = $allOpen; return $this; } @@ -205,7 +206,7 @@ public function setModelState($providerName, $modelId, $state) $this->states[$providerName] = []; } - $this->states[$providerName][$modelId] = (bool) $state; + $this->states[$providerName][$modelId] = $state; return $this; } diff --git a/src/DC/General.php b/src/DC/General.php index d91cacd69..d2f2f504b 100644 --- a/src/DC/General.php +++ b/src/DC/General.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -20,7 +20,8 @@ * @author Simon Kusterer * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,14 +31,17 @@ use Contao\DataContainer; use Contao\System; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Contao\Callback\Callbacks; use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\DataContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; use ContaoCommunityAlliance\DcGeneral\Factory\Event\PopulateEnvironmentEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -82,8 +86,11 @@ public function __construct($tableName, array $module = [], CacheInterface $cach $dispatcher = $this->getEventDispatcher(); $fetcher = \Closure::bind(function (PopulateEnvironmentEvent $event) use ($tableNameCallback) { + $definition = $event->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + // We need to capture the correct environment and save it for later use. - if ($tableNameCallback !== $event->getEnvironment()->getDataDefinition()->getName()) { + if ($tableNameCallback !== $definition->getName()) { return; } $this->objEnvironment = $event->getEnvironment(); @@ -97,11 +104,11 @@ public function __construct($tableName, array $module = [], CacheInterface $cach ->createDcGeneral(); $dispatcher->removeListener(PopulateEnvironmentEvent::NAME, $fetcher); + $clipboard = $this->getEnvironment()->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + // Load the clipboard. - $this - ->getEnvironment() - ->getClipboard() - ->loadFrom($this->getEnvironment()); + $clipboard->loadFrom($this->getEnvironment()); // Execute AJAX request, called from Backend::getBackendModule // we have to do this here, as otherwise the script will exit as it only checks for DC_Table and DC_File @@ -187,6 +194,7 @@ public function __get($name) { $environment = $this->getEnvironment(); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); switch ($name) { case 'id': @@ -200,7 +208,10 @@ public function __get($name) return ModelId::fromSerialized($inputProvider->getParameter($idParameter))->getId(); case 'table': - return $environment->getDataDefinition()->getName(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition->getName(); default: } @@ -214,7 +225,10 @@ public function __get($name) */ public function getName() { - return $this->getEnvironment()->getDataDefinition()->getName(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition->getName(); } /** @@ -263,7 +277,10 @@ public function getControllerHandler() */ public function __call($name, $arguments) { - return $this->getEnvironment()->getController()->handle(new Action($name, $arguments)); + $controller = $this->getEnvironment()->getController(); + assert($controller instanceof ControllerInterface); + + return $controller->handle(new Action($name, $arguments)); } /** @@ -274,9 +291,16 @@ public function __call($name, $arguments) protected function callAction() { $environment = $this->getEnvironment(); - $action = new Action($environment->getInputProvider()->getParameter('act') ?: 'showAll'); - return $environment->getController()->handle($action); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $action = new Action($inputProvider->getParameter('act') ?: 'showAll'); + + return $controller->handle($action); } /** diff --git a/src/Data/AbstractModel.php b/src/Data/AbstractModel.php index 3161d8f09..fb3ac23b0 100644 --- a/src/Data/AbstractModel.php +++ b/src/Data/AbstractModel.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,7 +25,7 @@ /** * Class AbstractModel. * Abstract base class for data provider models. - * This class implements the setter and getter for meta data. + * This class implements the setter and getter for meta-data. */ abstract class AbstractModel implements ModelInterface { @@ -38,16 +39,16 @@ abstract class AbstractModel implements ModelInterface /** * {@inheritdoc} */ - public function getMeta($metaName) + public function getMeta($strMetaName) { - return ($this->arrMetaInformation[$metaName] ?? null); + return ($this->arrMetaInformation[$strMetaName] ?? null); } /** * {@inheritdoc} */ - public function setMeta($metaName, $value) + public function setMeta($strMetaName, $varValue) { - $this->arrMetaInformation[$metaName] = $value; + $this->arrMetaInformation[$strMetaName] = $varValue; } } diff --git a/src/Data/ConfigInterface.php b/src/Data/ConfigInterface.php index c42ab7d63..0f2004135 100644 --- a/src/Data/ConfigInterface.php +++ b/src/Data/ConfigInterface.php @@ -154,7 +154,7 @@ public function setFilter($arrFilter); * * The returning array will be of 'property name' => 'ASC|DESC' nature. * - * @return array + * @return array */ public function getSorting(); @@ -163,7 +163,7 @@ public function getSorting(); * * The array must be of 'property name' => 'ASC|DESC' nature. * - * @param array $arrSorting The sorting array to use. + * @param array $arrSorting The sorting array to use. * * @return ConfigInterface */ @@ -197,7 +197,7 @@ public function get($strKey); /** * Set the additional information. * - * @param string $strKey The name of the information to retrieve. + * @param string $strKey The name of the information to retrieve. * * @param mixed $varValue The value to store. * diff --git a/src/Data/DataProviderInterface.php b/src/Data/DataProviderInterface.php index f402dc37f..bf2d883c1 100644 --- a/src/Data/DataProviderInterface.php +++ b/src/Data/DataProviderInterface.php @@ -86,7 +86,7 @@ public function fetch(ConfigInterface $config); * * @param ConfigInterface $config The configuration to use. * - * @return CollectionInterface|ModelInterface[]|string[] + * @return CollectionInterface|list */ public function fetchAll(ConfigInterface $config); @@ -170,7 +170,7 @@ public function saveVersion(ModelInterface $model, $username); * @param mixed $mixID The ID of the record. * @param mixed $mixVersion The ID of the version. * - * @return ModelInterface + * @return ModelInterface|null */ public function getVersion($mixID, $mixVersion); diff --git a/src/Data/DefaultCollection.php b/src/Data/DefaultCollection.php index ce3322007..f43c9e417 100644 --- a/src/Data/DefaultCollection.php +++ b/src/Data/DefaultCollection.php @@ -65,7 +65,7 @@ class DefaultCollection implements CollectionInterface */ public function length(): int { - return count($this->arrCollection); + return \count($this->arrCollection); } /** @@ -81,7 +81,7 @@ public function count(): int */ public function offsetExists($offset): bool { - return array_key_exists($offset, $this->arrCollection); + return \array_key_exists($offset, $this->arrCollection); } /** @@ -97,6 +97,7 @@ public function offsetGet($offset): ?ModelInterface */ public function offsetSet($offset, $value): void { + assert(\is_int($offset)); $this->arrCollection[$offset] = $value; } @@ -113,7 +114,7 @@ public function offsetUnset($offset): void */ public function get($index): ?ModelInterface { - if (array_key_exists($index, $this->arrCollection)) { + if (\array_key_exists($index, $this->arrCollection)) { return $this->arrCollection[$index]; } @@ -133,8 +134,8 @@ public function push(ModelInterface $model): void */ public function pop(): ?ModelInterface { - if (count($this->arrCollection)) { - return array_pop($this->arrCollection); + if (\count($this->arrCollection)) { + return \array_pop($this->arrCollection); } return null; @@ -146,7 +147,7 @@ public function pop(): ?ModelInterface public function unshift(ModelInterface $model): void { if ($model->hasProperties()) { - array_unshift($this->arrCollection, $model); + \array_unshift($this->arrCollection, $model); } } @@ -155,8 +156,8 @@ public function unshift(ModelInterface $model): void */ public function shift(): ?ModelInterface { - if (count($this->arrCollection)) { - return array_shift($this->arrCollection); + if (\count($this->arrCollection)) { + return \array_shift($this->arrCollection); } return null; @@ -177,7 +178,7 @@ public function insert($index, ModelInterface $model): void */ public function remove($mixedValue): void { - if (is_object($mixedValue)) { + if (\is_object($mixedValue)) { foreach ($this->arrCollection as $collectionIndex => $model) { if ($mixedValue === $model) { unset($this->arrCollection[$collectionIndex]); @@ -187,7 +188,7 @@ public function remove($mixedValue): void unset($this->arrCollection[$mixedValue]); } - $this->arrCollection = array_values($this->arrCollection); + $this->arrCollection = \array_values($this->arrCollection); } /** diff --git a/src/Data/DefaultConfig.php b/src/Data/DefaultConfig.php index 6e21ec849..f88a4d200 100644 --- a/src/Data/DefaultConfig.php +++ b/src/Data/DefaultConfig.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Tristan Lins * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -73,12 +74,12 @@ class DefaultConfig implements ConfigInterface * * @var array|null */ - protected $arrFilter; + protected $arrFilter = null; /** * The properties to use for sorting. * - * @var array(string => string) + * @var array */ protected $arrSorting = []; @@ -87,7 +88,7 @@ class DefaultConfig implements ConfigInterface * * @var array|null */ - protected $arrFields; + protected $arrFields = null; /** * Miscellaneous arbitrary data stored in the config. @@ -106,7 +107,6 @@ class DefaultConfig implements ConfigInterface */ private function __construct() { - return $this; } /** @@ -116,6 +116,7 @@ private function __construct() */ public static function init() { + /** @psalm-suppress UnsafeInstantiation */ return new static(); } @@ -132,13 +133,13 @@ public function getId() /** * Set a specific id for an element to be retrieved. * - * @param mixed $currentId The id of the element to be retrieved. + * @param mixed $mixId The id of the element to be retrieved. * * @return ConfigInterface */ - public function setId($currentId) + public function setId($mixId) { - $this->mixId = $currentId; + $this->mixId = $mixId; return $this; } @@ -147,16 +148,19 @@ public function setId($currentId) * Get list of specific ids to be retrieved. * * @return array + * + * @deprecated Use filters instead. */ public function getIds() { // @codingStandardsIgnoreStart @\trigger_error( 'The method setids in the DefaultConfig is deprecated since 2.1 and will be removed in 3.0.', - E_NOTICE + E_USER_NOTICE ); // @codingStandardsIgnoreEnd + /** @psalm-suppress DeprecatedProperty */ return $this->arrIds; } @@ -166,18 +170,21 @@ public function getIds() * @param array $arrIds The list of ids to be retrieved. * * @return ConfigInterface + * + * @deprecated Use filters instead. */ public function setIds($arrIds) { // @codingStandardsIgnoreStart @\trigger_error( - 'The method setids in the DefaultConfig is deprecated since 2.1 and will be removed in 3.0. - Use set filter + 'The method setids in the DefaultConfig is deprecated since 2.1 and will be removed in 3.0. + Use set filter $dataConfig->setFilter([[\'operation\' => \'IN\', \'property\' => \'id\', \'values\' => [4,3,2,1]]]).', - E_NOTICE + E_USER_NOTICE ); // @codingStandardsIgnoreEnd + /** @psalm-suppress DeprecatedProperty */ $this->arrIds = $arrIds; return $this; @@ -198,13 +205,13 @@ public function getIdOnly() /** * Set flag for return id only. * - * @param boolean $idOnly Boolean flag to determine that only Ids shall be returned when calling fetchAll(). + * @param boolean $blnIdOnly Boolean flag to determine that only Ids shall be returned when calling fetchAll(). * * @return DefaultConfig */ - public function setIdOnly($idOnly) + public function setIdOnly($blnIdOnly) { - $this->blnIdOnly = $idOnly; + $this->blnIdOnly = $blnIdOnly; return $this; } @@ -226,13 +233,13 @@ public function getStart() * * This is the offset to use for pagination. * - * @param integer $start Number of first element to return. + * @param integer $intStart Number of first element to return. * * @return ConfigInterface */ - public function setStart($start) + public function setStart($intStart) { - $this->intStart = $start; + $this->intStart = $intStart; return $this; } @@ -254,13 +261,13 @@ public function getAmount() * * This is the amount of items to return for pagination. * - * @param int $amount The amount to use. + * @param int $intAmount The amount to use. * * @return ConfigInterface */ - public function setAmount($amount) + public function setAmount($intAmount) { - $this->intAmount = $amount; + $this->intAmount = $intAmount; return $this; } @@ -278,13 +285,13 @@ public function getFilter() /** * Set the list with filter options. * - * @param array $filters The array containing the filter values. + * @param array $arrFilter The array containing the filter values. * * @return ConfigInterface */ - public function setFilter($filters) + public function setFilter($arrFilter) { - $this->arrFilter = $filters; + $this->arrFilter = $arrFilter; return $this; } @@ -294,7 +301,7 @@ public function setFilter($filters) * * The returning array will be of 'property name' => 'ASC|DESC' nature. * - * @return array + * @return array */ public function getSorting() { @@ -306,13 +313,13 @@ public function getSorting() * * The array must be of 'property name' => 'ASC|DESC' nature. * - * @param array $sortingProperties The sorting array to use. + * @param array $arrSorting The sorting array to use. * * @return ConfigInterface */ - public function setSorting($sortingProperties) + public function setSorting($arrSorting) { - $this->arrSorting = $sortingProperties; + $this->arrSorting = $arrSorting; return $this; } @@ -330,13 +337,13 @@ public function getFields() /** * Set the list of fields to be retrieved. * - * @param array $fields Array of property names. + * @param array $arrFields Array of property names. * * @return ConfigInterface */ - public function setFields($fields) + public function setFields($arrFields) { - $this->arrFields = $fields; + $this->arrFields = $arrFields; return $this; } @@ -344,14 +351,14 @@ public function setFields($fields) /** * Get the additional information. * - * @param string $informationName The name of the information to retrieve. + * @param string $strKey The name of the information to retrieve. * * @return mixed || null */ - public function get($informationName) + public function get($strKey) { - if (isset($this->arrData[$informationName])) { - return $this->arrData[$informationName]; + if (isset($this->arrData[$strKey])) { + return $this->arrData[$strKey]; } return null; @@ -360,14 +367,14 @@ public function get($informationName) /** * Set the additional information. * - * @param string $informationName The name of the information to retrieve. - * @param mixed $value The value to store. + * @param string $strKey The name of the information to retrieve. + * @param mixed $varValue The value to store. * * @return ConfigInterface */ - public function set($informationName, $value) + public function set($strKey, $varValue) { - $this->arrData[$informationName] = $value; + $this->arrData[$strKey] = $varValue; return $this; } diff --git a/src/Data/DefaultDataProvider.php b/src/Data/DefaultDataProvider.php index 9d89582a8..a8ff5f223 100644 --- a/src/Data/DefaultDataProvider.php +++ b/src/Data/DefaultDataProvider.php @@ -24,6 +24,7 @@ * @author Ingolf Steinhardt * @author Richard Henkenjohann * @author Alex Wuttke + * @author Ingolf Steinhardt * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource @@ -37,6 +38,7 @@ use Contao\System; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\Table; /** @@ -48,6 +50,9 @@ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) - There is no elegant way to reduce this class more without * reducing the interface. * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - Can not reduce without changing the interface. + * @SuppressWarnings(PHPMD.ExcessiveClassLength) - Can not reduce without changing the interface. + * + * @psalm-suppress MissingConstructor - properties will get set in setBaseConfig(). */ class DefaultDataProvider implements DataProviderInterface { @@ -56,7 +61,7 @@ class DefaultDataProvider implements DataProviderInterface * * @var string */ - protected $source; + protected $source = ''; /** * The Database instance. @@ -75,23 +80,23 @@ class DefaultDataProvider implements DataProviderInterface /** * The property that shall get populated with the current timestamp when saving data. * - * @var string + * @var string|null */ - protected $timeStampProperty = false; + protected $timeStampProperty = null; /** * The id generator to use (if any). * - * @var IdGeneratorInterface + * @var ?IdGeneratorInterface */ - protected $idGenerator; + protected $idGenerator = null; /** * The table schema. * - * @var Table + * @var ?Table */ - private $schema; + private ?Table $schema = null; /** * Retrieve the name of the id property. @@ -130,7 +135,7 @@ public function getTimeStampProperty() /** * Set the property name that shall get updated with the current time stamp when saving to the database. * - * @param boolean $timeStampField The property name or empty to clear. + * @param string $timeStampField The property name or empty to clear. * * @return DefaultDataProvider */ @@ -158,7 +163,7 @@ public function setIdGenerator($idGenerator) /** * Retrieve the id generator. * - * @return IdGeneratorInterface + * @return ?IdGeneratorInterface */ public function getIdGenerator() { @@ -199,6 +204,7 @@ public function setBaseConfig(array $config) throw new DcGeneralRuntimeException('Missing table name.'); } + /** @psalm-suppress DeprecatedMethod - bc layer. */ $this->fallbackFromDatabaseToConnection($config); if (isset($config['connection'])) { @@ -326,6 +332,7 @@ public function delete($item) protected function createModelFromDatabaseResult(array $result) { $model = $this->getEmptyModel(); + assert($model instanceof DefaultModel); foreach ($result as $key => $value) { if ($key === $this->idProperty) { @@ -368,6 +375,9 @@ public function fetch(ConfigInterface $config) } $result = $statement->fetchAssociative(); + if (false === $result) { + return null; + } return $this->createModelFromDatabaseResult($result); } @@ -387,13 +397,16 @@ public function fetchAll(ConfigInterface $config) if (0 !== $config->getAmount()) { $queryBuilder->setMaxResults($config->getAmount()); - $queryBuilder->setFirstResult($config->getStart() ?? 0); + $queryBuilder->setFirstResult($config->getStart()); } $collection = $this->getEmptyCollection(); $statement = $queryBuilder->executeQuery(); if (0 === $statement->rowCount()) { + if ($config->getIdOnly()) { + return []; + } return $collection; } @@ -420,7 +433,7 @@ public function getFilterOptions(ConfigInterface $config) { $internalConfig = $this->prefixDataProviderProperties($config); $properties = $internalConfig->getFields(); - if (1 !== \count($properties)) { + if (null === $properties || 1 !== \count($properties)) { throw new DcGeneralRuntimeException('objConfig must contain exactly one property to be retrieved.'); } $property = $properties[0]; @@ -436,7 +449,7 @@ public function getFilterOptions(ConfigInterface $config) $values = $statement->fetchAllAssociative(); $filterProperties = $config->getFields(); - if (1 !== \count($filterProperties)) { + if (null === $filterProperties || 1 !== \count($filterProperties)) { throw new DcGeneralRuntimeException('objConfig must contain exactly one property to be retrieved.'); } $filterProperty = $filterProperties[0]; @@ -462,9 +475,7 @@ public function getCount(ConfigInterface $config) $queryBuilder->from($this->source); DefaultDataProviderDBalUtils::addWhere($internalConfig, $queryBuilder); - $statement = $queryBuilder->executeQuery(); - - return $statement->fetchFirstColumn(); + return (int) $queryBuilder->executeQuery()->fetchOne(); } /** @@ -480,17 +491,14 @@ public function isUniqueValue($field, $new, $primaryId = null) $queryBuilder->setParameter($field, $new); $statement = $queryBuilder->executeQuery(); - $unique = $statement->fetchAssociative(); - - if (0 === $statement->rowCount()) { + $count = $statement->rowCount(); + if (0 === $count) { return true; } + $unique = $statement->fetchAssociative(); + assert(\is_array($unique)); - if (($primaryId === $unique['id']) && (1 === $statement->rowCount())) { - return true; - } - - return false; + return ($primaryId === $unique['id']) && (1 === $count); } /** @@ -505,7 +513,7 @@ public function resetFallback($field) ); // @codingStandardsIgnoreEnd - $this->connection->query('UPDATE ' . $this->source . ' SET ' . $field . ' = \'\'')->execute(); + $this->connection->executeQuery('UPDATE ' . $this->source . ' SET ' . $field . ' = \'\''); } /** @@ -627,7 +635,7 @@ private function convertModelToDataPropertyArray(ModelInterface $model, int $tim } if ($this->timeStampProperty) { - $data[$this->source . '.' . $this->getTimeStampProperty()] = $timestamp ?: \time(); + $data[$this->source . '.' . ($this->getTimeStampProperty() ?? '')] = $timestamp ?: \time(); } return $data; @@ -644,8 +652,8 @@ private function convertModelToDataPropertyArray(ModelInterface $model, int $tim private function insertModelIntoDatabase(ModelInterface $model, $timestamp = 0): void { $data = $this->convertModelToDataPropertyArray($model, $timestamp); - if ($this->getIdGenerator()) { - $model->setId($this->getIdGenerator()->generate()); + if ($generator = $this->getIdGenerator()) { + $model->setId($generator->generate()); $data[$this->idProperty] = $model->getId(); } @@ -711,8 +719,8 @@ public function saveEach(CollectionInterface $items, $timestamp = 0) */ public function fieldExists($columnName) { - if (!isset($this->schema)) { - $this->schema = $this->connection->getSchemaManager()->listTableDetails($this->source); + if (null === $this->schema) { + $this->schema = $this->connection->createSchemaManager()->introspectTable($this->source); } return $this->schema->hasColumn($columnName); @@ -740,7 +748,8 @@ public function getVersion($mixID, $mixVersion) return null; } - if (null === $version = $statement->fetchAllAssociative()) { + /** @var array{data: string}|false $version */ + if (false === $version = $statement->fetchAssociative()) { return null; } @@ -793,14 +802,14 @@ public function getVersions($mixID, $onlyActive = false) $statement = $queryBuilder->executeQuery(); if (0 === $statement->rowCount()) { - return null; + return $this->getEmptyCollection(); } $versions = $statement->fetchAllAssociative(); $collection = $this->getEmptyCollection(); - foreach ((array) $versions as $versionValue) { + foreach ($versions as $versionValue) { $model = $this->getEmptyModel(); $model->setId($mixID); @@ -839,7 +848,7 @@ public function saveVersion(ModelInterface $model, $username) $queryBuilder->setParameter('fromTable', $this->source); $statement = $queryBuilder->executeQuery(); - $count = $statement->fetchFirstColumn(); + $count = $statement->fetchOne(); $mixNewVersion = ((int) $count + 1); $mixData = $model->getPropertiesAsArray(); @@ -867,6 +876,8 @@ public function saveVersion(ModelInterface $model, $username) * @param mixed $mixVersion The version number to set active. * * @return void + * + * @throws Exception */ public function setVersionActive($mixID, $mixVersion) { @@ -906,7 +917,13 @@ public function getActiveVersion($mixID) return null; } - return $statement->fetchAllAssociative()['version']; + /** @var array{version: int}|false $row */ + $row = $statement->fetchAssociative(); + if (false === $row) { + return null; + } + + return $row['version']; } /** @@ -963,6 +980,9 @@ protected function insertUndo($sourceSQL, $saveSQL, $table) } $result = $statement->fetchAssociative(); + if (false === $result) { + return; + } // Save information in array. $parameters = []; @@ -994,7 +1014,10 @@ protected function insertUndo($sourceSQL, $saveSQL, $table) */ protected function getDefaultConnection() { - return System::getContainer()->get('database_connection'); + $connection = System::getContainer()->get('database_connection'); + assert($connection instanceof Connection); + + return $connection; } /** diff --git a/src/Data/DefaultDataProviderDBalUtils.php b/src/Data/DefaultDataProviderDBalUtils.php index 7abf47e2b..0200fffa9 100644 --- a/src/Data/DefaultDataProviderDBalUtils.php +++ b/src/Data/DefaultDataProviderDBalUtils.php @@ -26,6 +26,15 @@ /** * Static helper class for DefaultDataProvider. + * + * NOTE: Can't define filters recursively. See: https://github.com/vimeo/psalm/issues/2777 + * @psalm-type TSQLFilterAND=array{operation: 'AND', children: list} + * @psalm-type TSQLFilterOR=array{operation: 'OR', children: list} + * @psalm-type TSQLFilterCMP=array{operation: "="|">"|">="|"<"|"<="|"<>", property: string, value: string|int} + * @psalm-type TSQLFilterIN=array{operation: 'IN'|'NOT IN', property: string, values: list} + * @psalm-type TSQLFilterLIKE=array{operation: 'LIKE'|'NOT LIKE', property: string, value: string} + * @psalm-type TSQLFilterNULL=array{operation: 'IS NULL'|'IS NOT NULL', property: string, value: string} + * @psalm-type TSQLFilter=TSQLFilterAND|TSQLFilterOR|TSQLFilterCMP|TSQLFilterIN|TSQLFilterLIKE|TSQLFilterNULL */ class DefaultDataProviderDBalUtils { @@ -47,19 +56,20 @@ public static function addField(ConfigInterface $config, $idProperty, QueryBuild return; } - if (null !== $config->getFields()) { + if (null !== $fieldList = $config->getFields()) { + /** @psalm-suppress DeprecatedMethod - bc layer */ $connection = $queryBuilder->getConnection(); - $fields = implode( + $fields = \implode( ', ', - array_map( + \array_map( function ($field) use ($connection) { return $connection->quoteIdentifier($field); }, - $config->getFields() + $fieldList ) ); - if (false !== stripos($fields, 'DISTINCT') || \in_array($idProperty, $config->getFields(), true)) { + if (false !== \stripos($fields, 'DISTINCT') || \in_array($idProperty, $fieldList, true)) { $queryBuilder->select($fields); return; } @@ -78,8 +88,6 @@ function ($field) use ($connection) { * @param QueryBuilder $queryBuilder The query builder. * * @return void - * - * @internal param array $parameters The query parameters will get stored into this array. */ public static function addWhere($config, QueryBuilder $queryBuilder) { @@ -96,19 +104,20 @@ public static function addWhere($config, QueryBuilder $queryBuilder) */ public static function addSorting(ConfigInterface $config, QueryBuilder $queryBuilder) { - if (empty($config->getSorting()) || !is_array($config->getSorting())) { + $sorting = $config->getSorting(); + if (empty($sorting)) { return; } - foreach ($config->getSorting() as $sort => $order) { + foreach ($sorting as $sort => $order) { if (empty($sort)) { continue; } // array could be a simple field list or list of field => direction combinations. if (!empty($order)) { - $order = strtoupper($order); - if (!in_array($order, [DCGE::MODEL_SORTING_ASC, DCGE::MODEL_SORTING_DESC])) { + $order = \strtoupper($order); + if (!\in_array($order, [DCGE::MODEL_SORTING_ASC, DCGE::MODEL_SORTING_DESC])) { $sort = $order; $order = DCGE::MODEL_SORTING_ASC; } @@ -127,18 +136,17 @@ public static function addSorting(ConfigInterface $config, QueryBuilder $queryBu * @param QueryBuilder $queryBuilder The query builder. * * @return string The combined conditions. - * - * @internal param array $parameters The query parameters will get stored into this array. */ - private static function addFilter($config, QueryBuilder $queryBuilder) + private static function addFilter(ConfigInterface $config, QueryBuilder $queryBuilder): string { - $result = static::calculateSubFilter( - ['operation' => 'AND', 'children' => $config->getFilter()], - $queryBuilder - ); + /** @var list $filter */ + $filter = $config->getFilter() ?? []; + + /** @var TSQLFilterAND $andFilter */ + $andFilter = ['operation' => 'AND', 'children' => $filter]; // Combine filter syntax. - return $result ?: ''; + return static::calculateSubFilter($andFilter, $queryBuilder); } /** @@ -167,22 +175,16 @@ private static function addFilter($config, QueryBuilder $queryBuilder) * 'property' string (the name of a property) * 'value' literal - Wildcards * (Many) ? (One) * - * @param array $filter The filter to be combined to a valid SQL filter query. + * @param TSQLFilter $filter The filter to be combined to a valid SQL filter query. * @param QueryBuilder $queryBuilder The query builder. * * @return string The combined WHERE conditions. * * @throws DcGeneralRuntimeException If the sub filter has a sub filter. * @throws DcGeneralRuntimeException If the sub filter has a no filter. - * - * @internal param array $parameters The query parameters will get stored into this array. */ - private static function calculateSubFilter($filter, QueryBuilder $queryBuilder) + private static function calculateSubFilter(array $filter, QueryBuilder $queryBuilder): string { - if (!is_array($filter)) { - throw new DcGeneralRuntimeException('Error Processing sub filter: ' . var_export($filter, true), 1); - } - if (null !== ($rule = static::determineFilterAndOr($filter, $queryBuilder))) { return $rule; } @@ -209,16 +211,17 @@ private static function calculateSubFilter($filter, QueryBuilder $queryBuilder) /** * Determine sql filter for and/or. * - * @param array $filter The query filter. + * @param TSQLFilter $filter The query filter. * @param QueryBuilder $queryBuilder The query builder. * * @return string|null */ - private static function determineFilterAndOr(array $filter, QueryBuilder $queryBuilder) + private static function determineFilterAndOr(array $filter, QueryBuilder $queryBuilder): ?string { - if (!\in_array($filter['operation'], ['AND', 'OR'])) { + if (!\in_array($filter['operation'], ['AND', 'OR'], true)) { return null; } + /** @var TSQLFilterAND|TSQLFilterOR $filter */ static::filterAndOr($filter, $queryBuilder); @@ -228,16 +231,17 @@ private static function determineFilterAndOr(array $filter, QueryBuilder $queryB /** * Determine sql filter for comparing. * - * @param array $filter The query filter. + * @param TSQLFilter $filter The query filter. * @param QueryBuilder $queryBuilder The query builder. * * @return string|null */ - private static function determineFilterComparing(array $filter, QueryBuilder $queryBuilder) + private static function determineFilterComparing(array $filter, QueryBuilder $queryBuilder): ?string { - if (!\in_array($filter['operation'], ['=', '>', '>=', '<', '<=', '<>'])) { + if (!\in_array($filter['operation'], ['=', '>', '>=', '<', '<=', '<>'], true)) { return null; } + /** @var TSQLFilterCMP $filter */ return static::filterComparing($filter, $queryBuilder); } @@ -245,16 +249,17 @@ private static function determineFilterComparing(array $filter, QueryBuilder $qu /** * Determine sql filter for in or not in list. * - * @param array $filter The query filter. + * @param TSQLFilter $filter The query filter. * @param QueryBuilder $queryBuilder The query builder. * * @return string|null */ - private static function determineFilterInOrNotInList(array $filter, QueryBuilder $queryBuilder) + private static function determineFilterInOrNotInList(array $filter, QueryBuilder $queryBuilder): ?string { if (!\in_array($filter['operation'], ['IN', 'NOT IN'])) { return null; } + /** @var TSQLFilterIN $filter */ return static::filterInOrNotInList($filter, $queryBuilder); } @@ -262,7 +267,7 @@ private static function determineFilterInOrNotInList(array $filter, QueryBuilder /** * Determine sql filter for like or not like. * - * @param array $filter The query filter. + * @param TSQLFilter $filter The query filter. * @param QueryBuilder $queryBuilder The query builder. * * @return string|null @@ -272,6 +277,7 @@ private static function determineFilterLikeOrNotLike(array $filter, QueryBuilder if (!\in_array($filter['operation'], ['LIKE', 'NOT LIKE'])) { return null; } + /** @var TSQLFilterLIKE $filter */ return static::filterLikeOrNotLike($filter, $queryBuilder); } @@ -279,7 +285,7 @@ private static function determineFilterLikeOrNotLike(array $filter, QueryBuilder /** * Determine sql filter for is null or is not null. * - * @param array $filter The query filter. + * @param TSQLFilter $filter The query filter. * @param QueryBuilder $queryBuilder The query builder. * * @return string|null @@ -289,6 +295,7 @@ private static function determineFilterIsNullOrIsNotNull(array $filter, QueryBui if (!\in_array($filter['operation'], ['IS NULL', 'IS NOT NULL'])) { return null; } + /** @var TSQLFilterNULL $filter */ return static::filterIsNullOrIsNotNull($filter, $queryBuilder); } @@ -296,14 +303,12 @@ private static function determineFilterIsNullOrIsNotNull(array $filter, QueryBui /** * Build an AND or OR query. * - * @param array $operation The operation to convert. - * @param QueryBuilder $queryBuilder The query builder. + * @param TSQLFilterAND|TSQLFilterOR $operation The operation to convert. + * @param QueryBuilder $queryBuilder The query builder. * * @return void - * - * @internal param array $params The parameter array for the resulting query. */ - private static function filterAndOr($operation, QueryBuilder $queryBuilder) + private static function filterAndOr(array $operation, QueryBuilder $queryBuilder): void { $children = $operation['children']; @@ -311,8 +316,9 @@ private static function filterAndOr($operation, QueryBuilder $queryBuilder) return; } - $whereOperation = strtolower($operation['operation']) . 'Where'; + $whereOperation = \strtolower($operation['operation']) . 'Where'; + /** @var TSQLFilter $child */ foreach ($children as $child) { if ('' !== $child = static::calculateSubFilter($child, $queryBuilder)) { $queryBuilder->{$whereOperation}($child); @@ -323,14 +329,12 @@ private static function filterAndOr($operation, QueryBuilder $queryBuilder) /** * Build the sub query for a comparing operator like =,<,>. * - * @param array $operation The operation to apply. - * @param QueryBuilder $queryBuilder The query builder. + * @param TSQLFilterCMP $operation The operation to apply. + * @param QueryBuilder $queryBuilder The query builder. * * @return string - * - * @internal param array $params The parameters of the entire query. */ - private static function filterComparing($operation, QueryBuilder $queryBuilder) + private static function filterComparing(array $operation, QueryBuilder $queryBuilder): string { return $queryBuilder ->expr() @@ -344,16 +348,14 @@ private static function filterComparing($operation, QueryBuilder $queryBuilder) /** * Return the filter query for a "foo IN ('a', 'b')" filter. * - * @param array $operation The operation to apply. + * @param TSQLFilterIN $operation The operation to apply. * @param QueryBuilder $queryBuilder The query builder. * * @return string - * - * @internal param array $params The parameters of the entire query. */ - private static function filterInOrNotInList($operation, QueryBuilder $queryBuilder) + private static function filterInOrNotInList(array $operation, QueryBuilder $queryBuilder): string { - $expressionMethod = lcfirst(preg_replace('/\s+/', '', ucwords(strtolower($operation['operation'])))); + $expressionMethod = \lcfirst(\preg_replace('/\s+/', '', \ucwords(\strtolower($operation['operation'])))); $values = []; foreach ($operation['values'] as $index => $value) { @@ -372,16 +374,14 @@ private static function filterInOrNotInList($operation, QueryBuilder $queryBuild * * The searched value may contain the wildcards '*' and '?' which will get converted to proper SQL. * - * @param array $operation The operation to apply. - * @param QueryBuilder $queryBuilder The query builder. + * @param TSQLFilterLIKE $operation The operation to apply. + * @param QueryBuilder $queryBuilder The query builder. * * @return string - * - * @internal param array $params The parameters of the entire query. */ - private static function filterLikeOrNotLike($operation, QueryBuilder $queryBuilder) + private static function filterLikeOrNotLike(array $operation, QueryBuilder $queryBuilder): string { - $wildcards = str_replace(array('*', '?'), array('%', '_'), $operation['value']); + $wildcards = \str_replace(['*', '?'], ['%', '_'], $operation['value']); return $queryBuilder ->expr() @@ -389,20 +389,18 @@ private static function filterLikeOrNotLike($operation, QueryBuilder $queryBuild } /** - * Return the filter query for a "foo LIKE '%ba_r%'" filter. + * Return the filter query for a "foo IS NULL" filter. * * The searched value may contain the wildcards '*' and '?' which will get converted to proper SQL. * - * @param array $operation The operation to apply. - * @param QueryBuilder $queryBuilder The query builder. + * @param TSQLFilterNULL $operation The operation to apply. + * @param QueryBuilder $queryBuilder The query builder. * * @return string - * - * @internal param array $params The parameters of the entire query. */ - private static function filterIsNullOrIsNotNull($operation, QueryBuilder $queryBuilder) + private static function filterIsNullOrIsNotNull(array $operation, QueryBuilder $queryBuilder): string { - $expressionMethod = lcfirst(preg_replace('/\s+/', '', ucwords(strtolower($operation['operation'])))); + $expressionMethod = \lcfirst(\preg_replace('/\s+/', '', \ucwords(\strtolower($operation['operation'])))); return $queryBuilder ->expr() diff --git a/src/Data/DefaultDataProviderSqlUtils.php b/src/Data/DefaultDataProviderSqlUtils.php index 2faadf72b..e3c0b9286 100644 --- a/src/Data/DefaultDataProviderSqlUtils.php +++ b/src/Data/DefaultDataProviderSqlUtils.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,7 +36,6 @@ class DefaultDataProviderSqlUtils * Returns all values from $objConfig->getFields() as comma separated list. * * @param ConfigInterface $config The configuration to use. - * * @param string $idProperty The name of the id property. * * @return string @@ -47,8 +47,9 @@ public static function buildFieldQuery($config, $idProperty) if ($config->getIdOnly()) { return $idProperty; } + if (null !== $config->getFields()) { - $fields = \implode(', ', $config->getFields()); + $fields = \implode(', ', (array) $config->getFields()); if (false !== \stripos($fields, 'DISTINCT')) { return $fields; } @@ -62,7 +63,6 @@ public static function buildFieldQuery($config, $idProperty) * Build the WHERE clause for a configuration. * * @param ConfigInterface $config The configuration to use. - * * @param array $parameters The query parameters will get stored into this array. * * @return string The combined WHERE clause (including the word "WHERE"). @@ -94,7 +94,7 @@ public static function buildSortingQuery($config) $result = ''; $fields = []; - if (empty($sorting) || !\is_array($sorting)) { + if (empty($sorting)) { return ''; } foreach ($sorting as $field => $direction) { @@ -169,12 +169,8 @@ private static function buildFilterQuery($config, array &$parameters) * * @throws DcGeneralRuntimeException If an invalid filter entry is encountered. */ - private static function calculateSubfilter($filter, array &$parameters) + private static function calculateSubfilter(array $filter, array &$parameters): string { - if (!\is_array($filter)) { - throw new DcGeneralRuntimeException('Error Processing sub filter: ' . \var_export($filter, true), 1); - } - switch ($filter['operation']) { case 'AND': case 'OR': @@ -264,9 +260,11 @@ private static function filterInList($operation, &$params) */ private static function filterLike($operation, &$params) { - $wildcards = \str_replace(['*', '?'], ['%', '_'], $operation['value']); + $value = $operation['value']; + assert(\is_string($value)); + $wildcards = \str_replace(['*', '?'], ['%', '_'], $value); $params[] = $wildcards; - return \sprintf('(%s LIKE ?)', $operation['property'], $wildcards); + return \sprintf('(%s LIKE %s)', $operation['property'], $wildcards); } } diff --git a/src/Data/DefaultEditInformation.php b/src/Data/DefaultEditInformation.php index 6655e2fdb..e0677b5c2 100644 --- a/src/Data/DefaultEditInformation.php +++ b/src/Data/DefaultEditInformation.php @@ -37,7 +37,7 @@ class DefaultEditInformation implements EditInformationInterface /** * The model errors. * - * @var array + * @var array>> */ protected $modelErrors = []; @@ -71,11 +71,7 @@ public function getModelError(ModelInterface $model) { $modelId = ModelId::fromModel($model); - if (!isset($this->modelErrors[$modelId->getSerialized()])) { - return null; - } - - return $this->modelErrors[$modelId->getSerialized()]; + return $this->modelErrors[$modelId->getSerialized()] ?? []; } /** @@ -83,23 +79,24 @@ public function getModelError(ModelInterface $model) */ public function setModelError(ModelInterface $model, array $error, PropertyInterface $property) { - $modelId = ModelId::fromModel($model); + $modelId = ModelId::fromModel($model)->getSerialized(); + $propName = $property->getName(); - if (!isset($this->models[$modelId->getSerialized()])) { - $this->models[$modelId->getSerialized()] = $model; + if (!isset($this->models[$modelId])) { + $this->models[$modelId] = $model; } - if (!isset($this->modelErrors[$modelId->getSerialized()])) { - $this->modelErrors[$modelId->getSerialized()] = []; + if (!isset($this->modelErrors[$modelId])) { + $this->modelErrors[$modelId] = []; } - if (isset($this->modelErrors[$modelId->getSerialized()][$property->getName()])) { - $this->modelErrors[$modelId->getSerialized()][$property->getName()] = \array_merge( - (array) $this->modelErrors[$modelId->getSerialized()][$property->getName()], + if (isset($this->modelErrors[$modelId][$propName])) { + $this->modelErrors[$modelId][$propName] = \array_merge( + $this->modelErrors[$modelId][$propName], $error ); } else { - $this->modelErrors[$modelId->getSerialized()][$property->getName()] = $error; + $this->modelErrors[$modelId][$propName] = $error; } } diff --git a/src/Data/DefaultFilterOptionCollection.php b/src/Data/DefaultFilterOptionCollection.php index 394fd3b74..1941aac32 100644 --- a/src/Data/DefaultFilterOptionCollection.php +++ b/src/Data/DefaultFilterOptionCollection.php @@ -30,7 +30,7 @@ class DefaultFilterOptionCollection implements FilterOptionCollectionInterface /** * The language information stored in this collection. * - * @var array + * @var array */ protected $filterValues = []; @@ -47,7 +47,7 @@ public function add($filterKey, $filterValue) /** * Get a iterator for this collection. * - * @return \Traversable + * @return \Traversable */ public function getIterator(): \Traversable { diff --git a/src/Data/DefaultLanguageInformation.php b/src/Data/DefaultLanguageInformation.php index 4ca04e5eb..b0c55b8b9 100644 --- a/src/Data/DefaultLanguageInformation.php +++ b/src/Data/DefaultLanguageInformation.php @@ -35,7 +35,7 @@ class DefaultLanguageInformation implements LanguageInformationInterface /** * The ISO 3166 country code. * - * @var string + * @var null|string */ protected $country; @@ -72,8 +72,8 @@ public function getCountryCode() */ public function getLocale() { - if ($this->getCountryCode()) { - return $this->getLanguageCode() . '_' . $this->getCountryCode(); + if (null !== $country = $this->getCountryCode()) { + return $this->getLanguageCode() . '_' . $country; } return $this->getLanguageCode(); diff --git a/src/Data/DefaultLanguageInformationCollection.php b/src/Data/DefaultLanguageInformationCollection.php index ab9ad2302..e03a17c86 100644 --- a/src/Data/DefaultLanguageInformationCollection.php +++ b/src/Data/DefaultLanguageInformationCollection.php @@ -29,7 +29,7 @@ class DefaultLanguageInformationCollection implements LanguageInformationCollect /** * The language information stored in this collection. * - * @var LanguageInformationInterface[] + * @var list */ protected $languages = []; diff --git a/src/Data/DefaultModel.php b/src/Data/DefaultModel.php index 852459183..887de68b1 100644 --- a/src/Data/DefaultModel.php +++ b/src/Data/DefaultModel.php @@ -17,6 +17,7 @@ * @author Andreas Isaak * @author Patrick Kahl * @author Sven Baumann + * @author Ingolf Steinhardt * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource @@ -36,12 +37,12 @@ class DefaultModel extends AbstractModel /** * A list with all properties. * - * @var array + * @var array */ protected $arrProperties = []; /** - * The Id of this model. + * The ID of this model. * * @var mixed */ @@ -52,7 +53,7 @@ class DefaultModel extends AbstractModel * * @var string */ - protected $strProviderName; + protected $strProviderName = ''; /** * Copy this model, without the id. @@ -109,18 +110,18 @@ public function getPropertiesAsArray() /** * Set the id for this object. * - * NOTE: when the Id has been set once to a non null value, it can NOT be changed anymore. + * NOTE: when the ID has been set once to a non-null value, it can NOT be changed anymore. * - * Normally this should only be called from inside of the implementing provider. + * Normally this should only be called from inside the implementing provider. * - * @param mixed $mixID Could be a integer, string or anything else - depends on the provider implementation. + * @param mixed $mixId Could be integer, string or anything else - depends on the provider implementation. * * @return void */ - public function setID($mixID) + public function setId($mixId) { if (null === $this->mixID) { - $this->setIdRaw($mixID); + $this->setIdRaw($mixId); $this->setMeta(static::IS_CHANGED, true); } } @@ -130,13 +131,13 @@ public function setID($mixID) * * This method is not interfaced and MUST only be used for initial values from the data provider. * - * @param mixed $mixID Could be a integer, string or anything else - depends on the provider implementation. + * @param mixed $mixId Could be a integer, string or anything else - depends on the provider implementation. * * @return void */ - public function setIdRaw($mixID) + public function setIdRaw($mixId) { - $this->mixID = $mixID; + $this->mixID = $mixId; } /** @@ -157,16 +158,16 @@ public function setPropertyRaw($propertyName, $value) /** * Update the property value in the model. * - * @param string $propertyName The property name to be set. - * @param mixed $value The value to be set. + * @param string $strPropertyName The property name to be set. + * @param mixed $varValue The value to be set. * * @return void */ - public function setProperty($propertyName, $value) + public function setProperty($strPropertyName, $varValue) { - if ($value !== $this->getProperty($propertyName)) { + if ($varValue !== $this->getProperty($strPropertyName)) { $this->setMeta(static::IS_CHANGED, true); - $this->setPropertyRaw($propertyName, $value); + $this->setPropertyRaw($strPropertyName, $varValue); } } @@ -179,14 +180,12 @@ public function setProperty($propertyName, $value) */ public function setPropertiesAsArray($properties) { - if (\is_array($properties)) { - if (\array_key_exists('id', $properties)) { - unset($properties['id']); - } + if (\array_key_exists('id', $properties)) { + unset($properties['id']); + } - foreach ($properties as $propertyName => $value) { - $this->setProperty($propertyName, $value); - } + foreach ($properties as $propertyName => $value) { + $this->setProperty($propertyName, $value); } } diff --git a/src/Data/EditInformationInterface.php b/src/Data/EditInformationInterface.php index 8093912b6..4d8a2ddb7 100644 --- a/src/Data/EditInformationInterface.php +++ b/src/Data/EditInformationInterface.php @@ -39,7 +39,7 @@ public function hasAnyModelError(); * * @param ModelInterface $model The model. * - * @return array + * @return array> */ public function getModelError(ModelInterface $model); @@ -47,7 +47,7 @@ public function getModelError(ModelInterface $model); * Set the error information for the model. * * @param ModelInterface $model The model. - * @param array $error The error information. + * @param list $error The error information. * @param PropertyInterface $property The property. * * @return void diff --git a/src/Data/LanguageInformationCollectionInterface.php b/src/Data/LanguageInformationCollectionInterface.php index 3b9d34d47..04e8600bf 100644 --- a/src/Data/LanguageInformationCollectionInterface.php +++ b/src/Data/LanguageInformationCollectionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,8 @@ /** * This represents an iterable collection of Model elements. + * + * @extends \IteratorAggregate */ interface LanguageInformationCollectionInterface extends \IteratorAggregate, \Countable { diff --git a/src/Data/LanguageInformationInterface.php b/src/Data/LanguageInformationInterface.php index 3575469d2..6d2e85dc1 100644 --- a/src/Data/LanguageInformationInterface.php +++ b/src/Data/LanguageInformationInterface.php @@ -35,7 +35,7 @@ public function getLanguageCode(); /** * Retrieve the ISO 3166 country code. * - * @return string + * @return string|null */ public function getCountryCode(); diff --git a/src/Data/ModelId.php b/src/Data/ModelId.php index bffd44286..1fb554e9f 100644 --- a/src/Data/ModelId.php +++ b/src/Data/ModelId.php @@ -90,6 +90,7 @@ public function getId() */ public static function fromValues($dataProviderName, $modelId) { + /** @psalm-suppress UnsafeInstantiation */ return new static($dataProviderName, $modelId); } diff --git a/src/Data/ModelInterface.php b/src/Data/ModelInterface.php index 6bfe12513..37e2a9790 100644 --- a/src/Data/ModelInterface.php +++ b/src/Data/ModelInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Stefan Heimes * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,6 +29,8 @@ * Interface ModelInterface. * * This interface describes a model used in data providers. + * + * @extends \IteratorAggregate */ interface ModelInterface extends \IteratorAggregate { @@ -115,14 +118,14 @@ public function getId(); * * This method returns null if an unknown property is retrieved. * - * @param string $strPropertyName The property name to be retrieved. + * @param string $propertyName The property name to be retrieved. * * @return mixed The value of the given property. */ - public function getProperty($strPropertyName); + public function getProperty($propertyName); /** - * Fetch all properties from the model as an name => value array. + * Fetch all properties from the model as a name => value array. * * @return array */ @@ -140,11 +143,11 @@ public function getMeta($strMetaName); /** * Set the id for this object. * - * NOTE: when the Id has been set once to a non null value, it can NOT be changed anymore. + * NOTE: when the ID has been set once to a non-null value, it can NOT be changed anymore. * - * Normally this should only be called from inside of the implementing provider. + * Normally this should only be called from inside the implementing provider. * - * @param mixed $mixId Could be a integer, string or anything else - depends on the provider implementation. + * @param mixed $mixId Could be integer, string or anything else - depends on the provider implementation. * * @return void */ @@ -163,11 +166,11 @@ public function setProperty($strPropertyName, $varValue); /** * Update all properties in the model. * - * @param array $arrProperties The property values as name => value pairs. + * @param array $properties The property values as name => value pairs. * * @return void */ - public function setPropertiesAsArray($arrProperties); + public function setPropertiesAsArray($properties); /** * Update meta information in the model. diff --git a/src/Data/MultiLanguageDataProvider.php b/src/Data/MultiLanguageDataProvider.php index c90e8c3b2..a0f93f48f 100644 --- a/src/Data/MultiLanguageDataProvider.php +++ b/src/Data/MultiLanguageDataProvider.php @@ -28,6 +28,8 @@ * Implementation of a multi language Contao database data provider. * * The default language will be initialized to "en". + * + * @psalm-suppress PropertyNotSetInConstructor - properties will get set in setBaseConfig(). */ class MultiLanguageDataProvider extends DefaultDataProvider implements MultiLanguageDataProviderInterface { diff --git a/src/Data/MultiLanguageDataProviderInterface.php b/src/Data/MultiLanguageDataProviderInterface.php index bbdd5e698..4759735c6 100644 --- a/src/Data/MultiLanguageDataProviderInterface.php +++ b/src/Data/MultiLanguageDataProviderInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -49,11 +50,11 @@ public function getFallbackLanguage($mixID); /** * Set the current working language for the whole data provider. * - * @param string $strLanguage The new language, use short tag "2 chars like de, fr etc.". + * @param string $language The new language, use short tag "2 chars like de, fr etc.". * * @return DataProviderInterface */ - public function setCurrentLanguage($strLanguage); + public function setCurrentLanguage($language); /** * Get the current working language. diff --git a/src/Data/NoOpDataProvider.php b/src/Data/NoOpDataProvider.php index 6beb8f0ac..02bc77822 100644 --- a/src/Data/NoOpDataProvider.php +++ b/src/Data/NoOpDataProvider.php @@ -27,6 +27,8 @@ * * Base implementation of an no operational data provider. This data provider is simply a stub endpoint without any * logic at all. It is useful as parent class for drivers that only implement a fraction of all DcGeneral features. + * + * @psalm-suppress MissingConstructor - properties will get set in setBaseConfig(). */ class NoOpDataProvider implements DataProviderInterface { @@ -72,7 +74,7 @@ public function getEmptyCollection() } /** - * {@inheritdoc} + * @return DefaultFilterOptionCollection */ public function getEmptyFilterOptionCollection() { @@ -122,6 +124,7 @@ public function getCount(ConfigInterface $config) */ public function save(ModelInterface $item, $timestamp = 0) { + return $item; } /** @@ -161,7 +164,7 @@ public function getVersion($mixID, $mixVersion) */ public function getVersions($mixID, $onlyActive = false) { - return null; + return new DefaultCollection(); } /** diff --git a/src/Data/PropertyValueBag.php b/src/Data/PropertyValueBag.php index 166592c94..a9f9b0436 100644 --- a/src/Data/PropertyValueBag.php +++ b/src/Data/PropertyValueBag.php @@ -34,27 +34,27 @@ class PropertyValueBag implements PropertyValueBagInterface /** * All properties and its values in this bag. * - * @var array + * @var array */ protected $properties = []; /** * All properties that are marked as invalid and their error messages. * - * @var array + * @var array> */ protected $errors = []; /** * Create a new instance of a property bag. * - * @param array|null $properties The initial property values to use. + * @param iterable|null|mixed $properties The initial property values to use. * * @throws DcGeneralInvalidArgumentException If the passed properties aren't null or an array. */ public function __construct($properties = null) { - if (\is_array($properties) || $properties instanceof \Traversable) { + if (\is_iterable($properties)) { foreach ($properties as $property => $value) { $this->setPropertyValue($property, $value); } @@ -200,7 +200,7 @@ public function getInvalidPropertyNames() public function getPropertyValueErrors($property) { $this->requirePropertyValue($property); - return (array) ($this->errors[$property] ?? []); + return $this->errors[$property] ?? []; } /** @@ -248,6 +248,7 @@ public function offsetGet($offset): mixed */ public function offsetSet($offset, $value): void { + assert(\is_string($offset)); $this->setPropertyValue($offset, $value); } @@ -260,7 +261,7 @@ public function offsetUnset($offset): void } /** - * {@inheritdoc} + * @param string $name */ public function __isset($name) { @@ -268,7 +269,7 @@ public function __isset($name) } /** - * {@inheritdoc} + * @param string $name */ public function __get($name) { @@ -276,7 +277,8 @@ public function __get($name) } /** - * {@inheritdoc} + * @param string $name + * @param mixed $value */ public function __set($name, $value) { @@ -284,7 +286,7 @@ public function __set($name, $value) } /** - * {@inheritdoc} + * @param string $name */ public function __unset($name) { @@ -294,7 +296,7 @@ public function __unset($name) /** * Exports the {@link PropertyValueBag} to an array. * - * @return array + * @return array */ public function getArrayCopy() { diff --git a/src/Data/PropertyValueBagInterface.php b/src/Data/PropertyValueBagInterface.php index 100b5f307..41a7de095 100644 --- a/src/Data/PropertyValueBagInterface.php +++ b/src/Data/PropertyValueBagInterface.php @@ -24,6 +24,9 @@ /** * A generic bag containing properties and their values. + * + * @extends \IteratorAggregate + * @extends \ArrayAccess */ interface PropertyValueBagInterface extends \IteratorAggregate, \Countable, \ArrayAccess { @@ -103,9 +106,9 @@ public function isPropertyValueValid($property); /** * Mark a property as invalid and add an error message to the property. * - * @param string $property The name of the property to mark. - * @param string|array|mixed $error The error message to attach for this property. - * @param bool $append Append this error and keep previous errors (optional). + * @param string $property The name of the property to mark. + * @param string|list $error The error message to attach for this property. + * @param bool $append Append this error and keep previous errors (optional). * * @return PropertyValueBag */ @@ -132,7 +135,7 @@ public function getInvalidPropertyNames(); * * @param string $property The name of the property to retrieve the errors for. * - * @return array + * @return list */ public function getPropertyValueErrors($property); @@ -146,7 +149,7 @@ public function getInvalidPropertyErrors(); /** * Exports the {@link PropertyValueBag} to an array. * - * @return array + * @return array */ public function getArrayCopy(); } diff --git a/src/Data/TableRowsAsRecordsDataProvider.php b/src/Data/TableRowsAsRecordsDataProvider.php index 86fe9a2a4..2a789e93d 100644 --- a/src/Data/TableRowsAsRecordsDataProvider.php +++ b/src/Data/TableRowsAsRecordsDataProvider.php @@ -31,6 +31,8 @@ * Class TableRowsAsRecordsDataProvider. * * This data provider allows to map multiple rows of a SQL table into a single model for usage in a MultiColumnWizard. + * + * @psalm-suppress MissingConstructor - properties will get set in setBaseConfig(). */ class TableRowsAsRecordsDataProvider extends DefaultDataProvider { @@ -102,7 +104,7 @@ public function getSortingColumnProperty() * @throws DcGeneralException Throws always an exception telling that the method (see param $strMethod) must not be * called. * - * @return void + * @return never */ protected function youShouldNotCallMe($strMethod) { @@ -120,7 +122,7 @@ protected function youShouldNotCallMe($strMethod) * * @param mixed $item Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -164,6 +166,7 @@ public function fetch(ConfigInterface $config) $statement = $queryBuilder->executeQuery(); $model = $this->getEmptyModel(); + assert($model instanceof DefaultModel); $model->setID($config->getId()); if (0 < $statement->rowCount()) { $model->setPropertyRaw('rows', $statement->fetchAllAssociative()); @@ -177,7 +180,7 @@ public function fetch(ConfigInterface $config) * * @param ConfigInterface $config Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -193,7 +196,7 @@ public function fetchAll(ConfigInterface $config) * * @param ConfigInterface $config Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -211,7 +214,7 @@ public function getCount(ConfigInterface $config) * @param mixed $new Unused. * @param int $primaryId Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -227,7 +230,7 @@ public function isUniqueValue($field, $new, $primaryId = null) * * @param string $field Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -262,9 +265,6 @@ public function resetFallback($field) */ public function save(ModelInterface $item, $timestamp = 0, $recursive = false) { - if (!\is_int($timestamp)) { - throw new DcGeneralException('The parameter for this method has been change!'); - } $data = $item->getProperty('rows'); if (!($data && $item->getId())) { throw new DcGeneralException('invalid input data in model.', 1); @@ -292,7 +292,7 @@ public function save(ModelInterface $item, $timestamp = 0, $recursive = false) $sqlData, ['id' => $intId, $this->strGroupCol => $item->getId()] ); - $keep[] = $intId; + $keep[] = (string) $intId; continue; } @@ -301,8 +301,10 @@ public function save(ModelInterface $item, $timestamp = 0, $recursive = false) $sqlData[$this->strGroupCol] = $item->getId(); $this->connection->insert($this->source, $sqlData); - - $keep[] = $this->connection->lastInsertId($this->source); + if (false === $lastInsertId = $this->connection->lastInsertId($this->source)) { + throw new \RuntimeException('Failed to insert'); + } + $keep[] = (string) $lastInsertId; } // House keeping, kill the rest. @@ -323,7 +325,7 @@ public function save(ModelInterface $item, $timestamp = 0, $recursive = false) * @param CollectionInterface $items Unused. * @param int $timestamp Optional the timestamp. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -354,7 +356,7 @@ public function fieldExists($columnName) * @param mixed $mixID Unused. * @param mixed $mixVersion Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -371,14 +373,14 @@ public function getVersion($mixID, $mixVersion) * @param mixed $mixID Unused. * @param boolean $onlyActive Unused. * - * @return null + * @return CollectionInterface * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getVersions($mixID, $onlyActive = false) { // Sorry, versioning not supported. - return null; + return new DefaultCollection(); } /** @@ -387,7 +389,7 @@ public function getVersions($mixID, $onlyActive = false) * @param ModelInterface $model Unused. * @param string $username Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -404,7 +406,7 @@ public function saveVersion(ModelInterface $model, $username) * @param mixed $mixID Unused. * @param mixed $mixVersion Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -420,7 +422,7 @@ public function setVersionActive($mixID, $mixVersion) * * @param mixed $mixID Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -437,7 +439,7 @@ public function getActiveVersion($mixID) * @param ModelInterface $firstModel Unused. * @param ModelInterface $secondModel Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -455,7 +457,7 @@ public function sameModels($firstModel, $secondModel) * @param string $saveSQL Unused. * @param string $table Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * diff --git a/src/DataDefinition/AbstractConditionChain.php b/src/DataDefinition/AbstractConditionChain.php index 7facf388c..1deda72f6 100644 --- a/src/DataDefinition/AbstractConditionChain.php +++ b/src/DataDefinition/AbstractConditionChain.php @@ -122,7 +122,7 @@ public function setConjunction($conjunction) ); } - $this->conjunction = (string) $conjunction; + $this->conjunction = $conjunction; return $this; } diff --git a/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php b/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php index 2fc3bb393..78e6fee77 100644 --- a/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php +++ b/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Builder; use ContaoCommunityAlliance\DcGeneral\Factory\Event\BuildDataDefinitionEvent; +use LogicException; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -30,6 +32,8 @@ * Abstract base class for an data definition builder. * * To use it, implement the method build() and register the class to the event dispatcher. + * + * @psalm-suppress MissingConstructor - properties will get set in process(). */ abstract class AbstractEventDrivenDataDefinitionBuilder implements DataDefinitionBuilderInterface { @@ -44,16 +48,16 @@ abstract class AbstractEventDrivenDataDefinitionBuilder implements DataDefinitio /** * The event dispatcher currently calling. * - * @var EventDispatcherInterface + * @var EventDispatcherInterface|null */ - protected $dispatcher; + protected $dispatcher = null; /** * The name of the called event. * * @var string */ - protected $eventName; + protected $eventName = ''; /** * Retrieve the dispatcher. @@ -62,6 +66,10 @@ abstract class AbstractEventDrivenDataDefinitionBuilder implements DataDefinitio */ protected function getDispatcher() { + if (null === $this->dispatcher) { + throw new LogicException('Dispatcher not set.'); + } + return $this->dispatcher; } @@ -89,6 +97,7 @@ protected function getDispatchedEventName() */ public static function process(BuildDataDefinitionEvent $event, $eventName, $dispatcher) { + /** @psalm-suppress UnsafeInstantiation */ $builder = new static(); $builder->eventName = $eventName; $builder->dispatcher = $dispatcher; diff --git a/src/DataDefinition/DataProviderInformation.php b/src/DataDefinition/DataProviderInformation.php index adb9f287a..457788f87 100644 --- a/src/DataDefinition/DataProviderInformation.php +++ b/src/DataDefinition/DataProviderInformation.php @@ -30,14 +30,14 @@ class DataProviderInformation implements DataProviderInformationInterface * * @var string */ - protected $name; + protected $name = ''; /** * Flag determining if versioning is enabled for this provider or not. * * @var bool */ - protected $versioningEnabled; + protected $versioningEnabled = false; /** * {@inheritdoc} diff --git a/src/DataDefinition/DefaultContainer.php b/src/DataDefinition/DefaultContainer.php index fd77d85a2..5cf9b8215 100644 --- a/src/DataDefinition/DefaultContainer.php +++ b/src/DataDefinition/DefaultContainer.php @@ -57,7 +57,7 @@ class DefaultContainer implements ContainerInterface */ public function __construct($name) { - $this->name = (string) $name; + $this->name = $name; } /** @@ -171,9 +171,9 @@ public function hasBasicDefinition() /** * {@inheritdoc} */ - public function setBasicDefinition(BasicDefinitionInterface $definition) + public function setBasicDefinition(BasicDefinitionInterface $basicDefinition) { - return $this->setDefinition(BasicDefinitionInterface::NAME, $definition); + return $this->setDefinition(BasicDefinitionInterface::NAME, $basicDefinition); } /** @@ -181,7 +181,10 @@ public function setBasicDefinition(BasicDefinitionInterface $definition) */ public function getBasicDefinition() { - return $this->getDefinition(BasicDefinitionInterface::NAME); + $definition = $this->getDefinition(BasicDefinitionInterface::NAME); + assert($definition instanceof BasicDefinitionInterface); + + return $definition; } /** @@ -195,9 +198,9 @@ public function hasPropertiesDefinition() /** * {@inheritdoc} */ - public function setPropertiesDefinition(PropertiesDefinitionInterface $definition) + public function setPropertiesDefinition(PropertiesDefinitionInterface $propertiesDefinition) { - return $this->setDefinition(PropertiesDefinitionInterface::NAME, $definition); + return $this->setDefinition(PropertiesDefinitionInterface::NAME, $propertiesDefinition); } /** @@ -205,7 +208,10 @@ public function setPropertiesDefinition(PropertiesDefinitionInterface $definitio */ public function getPropertiesDefinition() { - return $this->getDefinition(PropertiesDefinitionInterface::NAME); + $definition = $this->getDefinition(PropertiesDefinitionInterface::NAME); + assert($definition instanceof PropertiesDefinitionInterface); + + return $definition; } /** @@ -219,9 +225,9 @@ public function hasPalettesDefinition() /** * {@inheritdoc} */ - public function setPalettesDefinition(PalettesDefinitionInterface $definition) + public function setPalettesDefinition(PalettesDefinitionInterface $palettesDefinition) { - return $this->setDefinition(PalettesDefinitionInterface::NAME, $definition); + return $this->setDefinition(PalettesDefinitionInterface::NAME, $palettesDefinition); } /** @@ -229,7 +235,10 @@ public function setPalettesDefinition(PalettesDefinitionInterface $definition) */ public function getPalettesDefinition() { - return $this->getDefinition(PalettesDefinitionInterface::NAME); + $definition = $this->getDefinition(PalettesDefinitionInterface::NAME); + assert($definition instanceof PalettesDefinitionInterface); + + return $definition; } /** @@ -253,7 +262,10 @@ public function setDataProviderDefinition(DataProviderDefinitionInterface $defin */ public function getDataProviderDefinition() { - return $this->getDefinition(DataProviderDefinitionInterface::NAME); + $definition = $this->getDefinition(DataProviderDefinitionInterface::NAME); + assert($definition instanceof DataProviderDefinitionInterface); + + return $definition; } /** @@ -277,7 +289,10 @@ public function setModelRelationshipDefinition(ModelRelationshipDefinitionInterf */ public function getModelRelationshipDefinition() { - return $this->getDefinition(ModelRelationshipDefinitionInterface::NAME); + $definition = $this->getDefinition(ModelRelationshipDefinitionInterface::NAME); + assert($definition instanceof ModelRelationshipDefinitionInterface); + + return $definition; } /** diff --git a/src/DataDefinition/Definition/BasicDefinitionInterface.php b/src/DataDefinition/Definition/BasicDefinitionInterface.php index 96db97707..85429a361 100644 --- a/src/DataDefinition/Definition/BasicDefinitionInterface.php +++ b/src/DataDefinition/Definition/BasicDefinitionInterface.php @@ -276,7 +276,7 @@ public function getRootEntries(); * * @param bool $dynamicParentTable The flag - if the data container is an dynamic. * - * @return bool + * @return BasicDefinitionInterface */ public function setDynamicParentTable($dynamicParentTable); diff --git a/src/DataDefinition/Definition/DataProviderDefinitionInterface.php b/src/DataDefinition/Definition/DataProviderDefinitionInterface.php index 61b93c42b..aefedf941 100644 --- a/src/DataDefinition/Definition/DataProviderDefinitionInterface.php +++ b/src/DataDefinition/Definition/DataProviderDefinitionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +26,9 @@ /** * This interface describes a collection of data provider information. + * + * @extends \IteratorAggregate + * @extends \ArrayAccess */ interface DataProviderDefinitionInterface extends DefinitionInterface, \IteratorAggregate, \Countable, \ArrayAccess { diff --git a/src/DataDefinition/Definition/DefaultBasicDefinition.php b/src/DataDefinition/Definition/DefaultBasicDefinition.php index 66e84858c..820a1ba34 100644 --- a/src/DataDefinition/Definition/DefaultBasicDefinition.php +++ b/src/DataDefinition/Definition/DefaultBasicDefinition.php @@ -94,9 +94,9 @@ class DefaultBasicDefinition implements BasicDefinitionInterface /** * Determines if the view shall switch automatically into edit mode. * - * @var bool|null + * @var bool */ - protected $switchToEditEnabled; + protected $switchToEditEnabled = false; /** * The ids of the root entries. @@ -224,6 +224,8 @@ public function getAdditionalFilter($dataProvider = null) public function setEditOnlyMode($value) { $this->isEditOnlyMode = $value; + + return $this; } /** diff --git a/src/DataDefinition/Definition/DefaultDataProviderDefinition.php b/src/DataDefinition/Definition/DefaultDataProviderDefinition.php index 18bc04a60..e7d72205f 100644 --- a/src/DataDefinition/Definition/DefaultDataProviderDefinition.php +++ b/src/DataDefinition/Definition/DefaultDataProviderDefinition.php @@ -35,7 +35,7 @@ class DefaultDataProviderDefinition implements DataProviderDefinitionInterface /** * The data provider information stored in the definition. * - * @var DataProviderInformationInterface[] + * @var array */ protected $information = []; @@ -58,6 +58,8 @@ public function addInformation($information) } $this->information[$name] = $information; + + return $this; } /** @@ -80,10 +82,6 @@ protected function makeName($information) $information = $information->getName(); } - if (!\is_string($information)) { - throw new DcGeneralInvalidArgumentException('Invalid value passed.'); - } - return $information; } @@ -93,6 +91,8 @@ protected function makeName($information) public function removeInformation($information) { unset($this->information[$this->makeName($information)]); + + return $this; } /** @@ -101,6 +101,8 @@ public function removeInformation($information) public function setInformation($name, $information) { $this->information[$name] = $information; + + return $this; } /** @@ -164,6 +166,7 @@ public function offsetGet($offset): mixed */ public function offsetSet($offset, $value): void { + assert(\is_string($offset)); $this->setInformation($offset, $value); } @@ -176,7 +179,7 @@ public function offsetUnset($offset): void } /** - * {@inheritdoc} + * @param string $name */ public function __isset($name) { @@ -184,7 +187,7 @@ public function __isset($name) } /** - * {@inheritdoc} + * @param string $name */ public function __get($name) { @@ -192,7 +195,8 @@ public function __get($name) } /** - * {@inheritdoc} + * @param string $name + * @param mixed $value */ public function __set($name, $value) { @@ -200,7 +204,7 @@ public function __set($name, $value) } /** - * {@inheritdoc} + * @param string $name */ public function __unset($name) { diff --git a/src/DataDefinition/Definition/DefaultModelRelationshipDefinition.php b/src/DataDefinition/Definition/DefaultModelRelationshipDefinition.php index 151ae9cfc..54db3a076 100644 --- a/src/DataDefinition/Definition/DefaultModelRelationshipDefinition.php +++ b/src/DataDefinition/Definition/DefaultModelRelationshipDefinition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,9 +32,9 @@ class DefaultModelRelationshipDefinition implements ModelRelationshipDefinitionI /** * The root condition relationship. * - * @var RootConditionInterface + * @var RootConditionInterface|null */ - protected $rootCondition; + protected $rootCondition = null; /** * A collection of parent child conditions. diff --git a/src/DataDefinition/Definition/DefaultPropertiesDefinition.php b/src/DataDefinition/Definition/DefaultPropertiesDefinition.php index 0f655987b..770913ea6 100644 --- a/src/DataDefinition/Definition/DefaultPropertiesDefinition.php +++ b/src/DataDefinition/Definition/DefaultPropertiesDefinition.php @@ -106,7 +106,7 @@ public function hasProperty($name) /** * {@inheritdoc} * - * @throws DcGeneralInvalidArgumentException When an a property with the given name has not been registered. + * @throws DcGeneralInvalidArgumentException When a property with the given name has not been registered. */ public function getProperty($name) { diff --git a/src/DataDefinition/Definition/Properties/DefaultProperty.php b/src/DataDefinition/Definition/Properties/DefaultProperty.php index afb822805..316d0b53d 100644 --- a/src/DataDefinition/Definition/Properties/DefaultProperty.php +++ b/src/DataDefinition/Definition/Properties/DefaultProperty.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,21 +36,21 @@ class DefaultProperty implements PropertyInterface, EmptyValueAwarePropertyInter * * @var string */ - protected $name; + protected $name = ''; /** * The label of the property. * * @var string */ - protected $label; + protected $label = ''; /** * The description of the property. * * @var string */ - protected $description; + protected $description = ''; /** * The default value of the property. @@ -63,60 +64,35 @@ class DefaultProperty implements PropertyInterface, EmptyValueAwarePropertyInter * * @var bool */ - protected $excluded; + protected $excluded = false; /** * Flag if this property shall be searchable. * * @var bool */ - protected $searchable; + protected $searchable = false; /** * Flag if this property shall be sortable. * * @var bool */ - protected $sortable; + protected $sortable = false; /** * Flag if this property shall be filterable. * * @var bool */ - protected $filterable; - - /** - * The grouping mode for this property. - * - * See ListingConfigInterface::GROUP_* flags. - * - * @var string - */ - protected $groupingMode; - - /** - * The grouing length of this property. See grouping mode. - * - * @var string - */ - protected $groupingLength; - - /** - * The sorting mode for this property. - * - * See ListingConfigInterface::SORT_* flags. - * - * @var string - */ - protected $sortingMode; + protected $filterable = false; /** * The input widget type to use. * * @var string */ - protected $widgetType; + protected $widgetType = ''; /** * The value options for this property. @@ -130,7 +106,7 @@ class DefaultProperty implements PropertyInterface, EmptyValueAwarePropertyInter * * @var string */ - protected $explanation; + protected $explanation = ''; /** * The extra information for this property. diff --git a/src/DataDefinition/Definition/PropertiesDefinitionInterface.php b/src/DataDefinition/Definition/PropertiesDefinitionInterface.php index d166d0063..825e374ab 100644 --- a/src/DataDefinition/Definition/PropertiesDefinitionInterface.php +++ b/src/DataDefinition/Definition/PropertiesDefinitionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +26,8 @@ /** * This interface describes the data definition that holds all property information. + * + * @extends \IteratorAggregate */ interface PropertiesDefinitionInterface extends DefinitionInterface, \IteratorAggregate { diff --git a/src/DataDefinition/Definition/View/Command.php b/src/DataDefinition/Definition/View/Command.php index 2c72c6b3d..a275b1279 100644 --- a/src/DataDefinition/Definition/View/Command.php +++ b/src/DataDefinition/Definition/View/Command.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class Command implements CommandInterface * * @var string */ - protected $name; + protected $name = ''; /** * The parameters for the command. @@ -47,14 +48,14 @@ class Command implements CommandInterface * * @var string */ - protected $label; + protected $label = ''; /** * The description text for the command. * * @var string */ - protected $description; + protected $description = ''; /** * The extra data for the command. @@ -68,7 +69,7 @@ class Command implements CommandInterface * * @var bool */ - protected $disabled; + protected $disabled = false; /** * Create a new instance. @@ -84,7 +85,7 @@ public function __construct() */ public function setName($name) { - $this->name = (string) $name; + $this->name = $name; return $this; } @@ -120,7 +121,7 @@ public function getParameters() */ public function setLabel($label) { - $this->label = (string) $label; + $this->label = $label; return $this; } @@ -138,7 +139,7 @@ public function getLabel() */ public function setDescription($description) { - $this->description = (string) $description; + $this->description = $description; return $this; } diff --git a/src/DataDefinition/Definition/View/CommandCollection.php b/src/DataDefinition/Definition/View/CommandCollection.php index 994ba9a81..d1c3a0a48 100644 --- a/src/DataDefinition/Definition/View/CommandCollection.php +++ b/src/DataDefinition/Definition/View/CommandCollection.php @@ -34,7 +34,7 @@ class CommandCollection implements CommandCollectionInterface /** * The commands contained within the collection. * - * @var CommandInterface[] + * @var array */ protected $commands = []; @@ -123,9 +123,9 @@ public function addCommand(CommandInterface $command, CommandInterface $before = $position = \array_search($beforeHash, $hashes); $this->commands = \array_merge( - \array_slice($this->commands, 0, $position), + \array_slice($this->commands, 0, (int) $position), [$hash => $command], - \array_slice($this->commands, $position) + \array_slice($this->commands, (int) $position) ); } else { throw new DcGeneralInvalidArgumentException( diff --git a/src/DataDefinition/Definition/View/CommandCollectionInterface.php b/src/DataDefinition/Definition/View/CommandCollectionInterface.php index b78e9ef5d..dd9f2bcb8 100644 --- a/src/DataDefinition/Definition/View/CommandCollectionInterface.php +++ b/src/DataDefinition/Definition/View/CommandCollectionInterface.php @@ -124,7 +124,7 @@ public function getCommandNamed($name); /** * Get commands from this collection. * - * @return CommandInterface[]|array + * @return array */ public function getCommands(); } diff --git a/src/DataDefinition/Definition/View/CommandInterface.php b/src/DataDefinition/Definition/View/CommandInterface.php index f0faf407a..bf40b27a6 100644 --- a/src/DataDefinition/Definition/View/CommandInterface.php +++ b/src/DataDefinition/Definition/View/CommandInterface.php @@ -72,7 +72,7 @@ public function setLabel($label); /** * Return the label of the command. * - * @return array + * @return string */ public function getLabel(); @@ -88,7 +88,7 @@ public function setDescription($description); /** * Return the description of the command. * - * @return array + * @return string */ public function getDescription(); diff --git a/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php b/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php index 62ee4ae9a..57bec044a 100644 --- a/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php +++ b/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php @@ -117,6 +117,8 @@ public function markDefault($information) } $this->default = $information; + + return $this; } /** diff --git a/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php b/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php index 4e2ba6efb..8b1362757 100644 --- a/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php +++ b/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php @@ -31,7 +31,7 @@ class DefaultGroupAndSortingInformation implements GroupAndSortingInformationInt * * @var string */ - protected $property; + protected $property = ''; /** * The sorting method to use. @@ -59,7 +59,7 @@ class DefaultGroupAndSortingInformation implements GroupAndSortingInformationInt * * @var bool */ - protected $manualSorting; + protected $manualSorting = false; /** * {@inheritDoc} diff --git a/src/DataDefinition/Definition/View/DefaultListingConfig.php b/src/DataDefinition/Definition/View/DefaultListingConfig.php index cfc5b3753..6131ec8d5 100644 --- a/src/DataDefinition/Definition/View/DefaultListingConfig.php +++ b/src/DataDefinition/Definition/View/DefaultListingConfig.php @@ -40,11 +40,11 @@ class DefaultListingConfig implements ListingConfigInterface protected $groupAndSorting; /** - * The properties to display in the heder (parented mode only). + * The properties to display in the header (parented mode only). * - * @var array|null + * @var list */ - protected $headerProperties; + protected $headerProperties = []; /** * The root icon to use (hierarchical mode only). @@ -70,7 +70,7 @@ class DefaultListingConfig implements ListingConfigInterface /** * The item formatter to use. * - * @var DefaultModelFormatterConfig[] + * @var array */ protected $itemFormatter = []; diff --git a/src/DataDefinition/Definition/View/DefaultModelFormatterConfig.php b/src/DataDefinition/Definition/View/DefaultModelFormatterConfig.php index 22360968f..2e14bdf9a 100644 --- a/src/DataDefinition/Definition/View/DefaultModelFormatterConfig.php +++ b/src/DataDefinition/Definition/View/DefaultModelFormatterConfig.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -45,7 +46,7 @@ class DefaultModelFormatterConfig implements ModelFormatterConfigInterface * * @var int|null */ - protected $maxLength; + protected $maxLength = null; /** * {@inheritDoc} @@ -70,7 +71,7 @@ public function getPropertyNames() */ public function setFormat($format) { - $this->format = (string) $format; + $this->format = $format; return $this; } @@ -88,7 +89,7 @@ public function getFormat() */ public function setMaxLength($maxLength) { - $this->maxLength = (null !== $maxLength) ? (int) $maxLength : null; + $this->maxLength = (null !== $maxLength) ? $maxLength : null; return $this; } diff --git a/src/DataDefinition/Definition/View/DefaultPanelRow.php b/src/DataDefinition/Definition/View/DefaultPanelRow.php index 451b28ddd..723349066 100644 --- a/src/DataDefinition/Definition/View/DefaultPanelRow.php +++ b/src/DataDefinition/Definition/View/DefaultPanelRow.php @@ -33,7 +33,7 @@ class DefaultPanelRow implements PanelRowInterface /** * The contained elements. * - * @var ElementInformationInterface[] + * @var list */ protected $elements = []; @@ -93,7 +93,7 @@ function ($element) use ($indexOrNameOrInstance) { break; } } - } elseif (\is_numeric($indexOrNameOrInstance)) { + } else { unset($this->elements[$indexOrNameOrInstance]); } @@ -111,18 +111,14 @@ public function hasElement($instanceOrName) return \in_array($instanceOrName, $this->elements); } - if (\is_string($instanceOrName)) { - foreach ($this as $element) { - /** @var ElementInformationInterface $element */ - if ($instanceOrName === $element->getName()) { - return true; - } + foreach ($this as $element) { + /** @var ElementInformationInterface $element */ + if ($instanceOrName === $element->getName()) { + return true; } - - return false; } - throw new DcGeneralInvalidArgumentException('Invalid value for element name given.'); + return false; } /** @@ -148,8 +144,7 @@ public function getElement($indexOrName) return $element; } } - } elseif (!\is_numeric($indexOrName)) { - throw new DcGeneralInvalidArgumentException('Invalid value for element name given.'); + throw new DcGeneralInvalidArgumentException('Value out of bounds: ' . $indexOrName . '.'); } if (!isset($this->elements[$indexOrName])) { diff --git a/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php b/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php index cb1bc81eb..1ccfca0a3 100644 --- a/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php +++ b/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php @@ -31,7 +31,7 @@ class DefaultPanelRowCollection implements PanelRowCollectionInterface /** * The panel rows. * - * @var PanelRowInterface[] + * @var list */ protected $rows = []; diff --git a/src/DataDefinition/Definition/View/ListingConfigInterface.php b/src/DataDefinition/Definition/View/ListingConfigInterface.php index d7af4a9b3..373d381b1 100644 --- a/src/DataDefinition/Definition/View/ListingConfigInterface.php +++ b/src/DataDefinition/Definition/View/ListingConfigInterface.php @@ -56,7 +56,7 @@ public function getGroupAndSortingDefinition(); /** * Set the list of parent's model property names. * - * @param array $value The property names to use. + * @param list $value The property names to use. * * @return ListingConfigInterface */ @@ -65,7 +65,7 @@ public function setHeaderPropertyNames($value); /** * Return a list of parent's model property names, which are shown above the item list. * - * @return array + * @return list */ public function getHeaderPropertyNames(); diff --git a/src/DataDefinition/Definition/View/Panel/DefaultFilterElementInformation.php b/src/DataDefinition/Definition/View/Panel/DefaultFilterElementInformation.php index df0b36f12..14e6a8f4b 100644 --- a/src/DataDefinition/Definition/View/Panel/DefaultFilterElementInformation.php +++ b/src/DataDefinition/Definition/View/Panel/DefaultFilterElementInformation.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class DefaultFilterElementInformation implements FilterElementInformationInterfa * * @var string */ - protected $propertyName; + protected $propertyName = ''; /** * {@inheritDoc} @@ -49,6 +50,8 @@ public function getName() public function setPropertyName($propertyName) { $this->propertyName = $propertyName; + + return $this; } /** diff --git a/src/DataDefinition/Definition/View/Panel/DefaultSearchElementInformation.php b/src/DataDefinition/Definition/View/Panel/DefaultSearchElementInformation.php index 6fcf0efc1..1324105c5 100644 --- a/src/DataDefinition/Definition/View/Panel/DefaultSearchElementInformation.php +++ b/src/DataDefinition/Definition/View/Panel/DefaultSearchElementInformation.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class DefaultSearchElementInformation implements SearchElementInformationInterfa * * @var array */ - protected $properties; + protected $properties = []; /** * {@inheritDoc} @@ -49,6 +50,8 @@ public function getName() public function addProperty($propertyName) { $this->properties[] = $propertyName; + + return $this; } /** diff --git a/src/DataDefinition/Definition/View/PanelRowCollectionInterface.php b/src/DataDefinition/Definition/View/PanelRowCollectionInterface.php index ec9d33d39..c862b9c07 100644 --- a/src/DataDefinition/Definition/View/PanelRowCollectionInterface.php +++ b/src/DataDefinition/Definition/View/PanelRowCollectionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,6 +24,8 @@ /** * This interface describes a panel row collection. + * + * @extends \IteratorAggregate */ interface PanelRowCollectionInterface extends \IteratorAggregate { @@ -34,7 +37,7 @@ interface PanelRowCollectionInterface extends \IteratorAggregate * * Note that each panel element decides its name on its own. * - * @return array + * @return list> */ public function getRows(); diff --git a/src/DataDefinition/Definition/View/PanelRowInterface.php b/src/DataDefinition/Definition/View/PanelRowInterface.php index b3a754798..96bfcd1ec 100644 --- a/src/DataDefinition/Definition/View/PanelRowInterface.php +++ b/src/DataDefinition/Definition/View/PanelRowInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +26,8 @@ /** * This interface describes a panel row definition. + * + * @extends \IteratorAggregate */ interface PanelRowInterface extends \IteratorAggregate { @@ -34,7 +37,7 @@ interface PanelRowInterface extends \IteratorAggregate * This will return the following for example: * array('filter[prop1]', 'filter[prop2]', 'search', 'limit') * - * @return array + * @return list */ public function getElements(); diff --git a/src/DataDefinition/Definition/View/ToggleCommand.php b/src/DataDefinition/Definition/View/ToggleCommand.php index d53452be8..fb4585642 100644 --- a/src/DataDefinition/Definition/View/ToggleCommand.php +++ b/src/DataDefinition/Definition/View/ToggleCommand.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,7 +32,7 @@ class ToggleCommand extends Command implements ToggleCommandInterface * * @var string */ - protected $property; + protected $property = ''; /** * The toggle command is an inverse command. @@ -65,7 +66,8 @@ public function getToggleProperty() */ public function setInverse($inverse) { - $this->inverse = (bool) $inverse; + $this->inverse = $inverse; + return $this; } diff --git a/src/DataDefinition/Definition/View/TranslatedToggleCommand.php b/src/DataDefinition/Definition/View/TranslatedToggleCommand.php index f6cb5df8c..fa7737f9b 100644 --- a/src/DataDefinition/Definition/View/TranslatedToggleCommand.php +++ b/src/DataDefinition/Definition/View/TranslatedToggleCommand.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,7 +31,7 @@ class TranslatedToggleCommand extends ToggleCommand implements TranslatedToggleC * * @var string */ - protected $language; + protected $language = ''; /** * {@inheritDoc} diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder.php index 6b1e0ac79..cba1b797e 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder.php @@ -60,13 +60,10 @@ class FilterBuilder */ public function __construct($filter = [], $isRoot = false) { - if (!\is_array($filter)) { - throw new DcGeneralInvalidArgumentException( - 'FilterBuilder needs a valid filter array ' . \gettype($filter) . 'given' - ); - } + $filters = static::getBuilderFromArray(['operation' => 'AND', 'children' => $filter], $this); + assert($filters instanceof AndFilterBuilder); - $this->filters = static::getBuilderFromArray(['operation' => 'AND', 'children' => $filter], $this); + $this->filters = $filters; $this->isRootFilter = $isRoot; } @@ -114,6 +111,7 @@ public static function getBuilderFromArray($filter, $builder) */ public static function fromArray($filter = []) { + /** @psalm-suppress UnsafeInstantiation */ return new static($filter, false); } @@ -126,6 +124,7 @@ public static function fromArray($filter = []) */ public static function fromArrayForRoot($filter = []) { + /** @psalm-suppress UnsafeInstantiation */ return new static($filter, true); } diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/AndFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/AndFilterBuilder.php index 260b04b57..6fa2c56fe 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/AndFilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/AndFilterBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class AndFilterBuilder extends FilterBuilderWithChildren /** * Create a new instance. * - * @param array $children The initial children to absorb. + * @param list $children The initial children to absorb. * * @throws DcGeneralInvalidArgumentException When invalid children have been passed. */ @@ -58,7 +59,7 @@ public function append($filters) } if ($filters instanceof AndFilterBuilder) { - foreach (clone $filters as $filter) { + foreach ($filters as $filter) { $this->add(clone $filter); } diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/BaseComparingFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/BaseComparingFilterBuilder.php index 2472c7a59..24fb91a09 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/BaseComparingFilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/BaseComparingFilterBuilder.php @@ -35,42 +35,35 @@ class BaseComparingFilterBuilder extends BaseFilterBuilder * * @var string */ - protected $operation; + protected $operation = ''; /** * The property to be checked. * * @var string */ - protected $property; - - /** - * The property to be checked. - * - * @var string - */ - protected $remoteProperty; + protected $property = ''; /** * The value to compare against. * * @var mixed */ - protected $value; + protected $value = ''; /** * Flag determining if the passed value is a remote property name or not. * * @var bool */ - protected $isRemote; + protected $isRemote = false; /** * Flag determining if the remote value is a property or literal value. * * @var bool */ - protected $isRemoteProp; + protected $isRemoteProp = false; /** * Create a new instance. @@ -124,6 +117,7 @@ public static function fromArray($array) throw new DcGeneralInvalidArgumentException('Invalid filter array provided ' . \var_export($array, true)); } + /** @psalm-suppress UnsafeInstantiation */ return new static($property, $value, $isRemote, $isRemoteProp); } diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/BaseFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/BaseFilterBuilder.php index bb6f77638..3575259da 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/BaseFilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/BaseFilterBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,16 +34,16 @@ abstract class BaseFilterBuilder /** * The filter builder holding the scope. * - * @var FilterBuilder + * @var null|FilterBuilder */ - protected $builder; + protected $builder = null; /** - * The current parenting Builder. + * The current parenting builder. * - * @var FilterBuilderWithChildren + * @var null|FilterBuilderWithChildren */ - protected $parent; + protected $parent = null; /** * Get the filter builder. @@ -51,6 +52,10 @@ abstract class BaseFilterBuilder */ public function getBuilder() { + if (null === $this->builder) { + throw new \LogicException('Builder has not been set.'); + } + return $this->builder; } @@ -73,7 +78,7 @@ public function setBuilder($builder) * * @param FilterBuilderWithChildren $parent The new parent. * - * @return FilterBuilderWithChildren + * @return BaseFilterBuilder */ public function setParent(FilterBuilderWithChildren $parent) { @@ -94,6 +99,10 @@ public function setParent(FilterBuilderWithChildren $parent) */ public function getParent() { + if (null === $this->parent) { + throw new \LogicException('Parent has not been set.'); + } + return $this->parent; } @@ -111,7 +120,10 @@ abstract public function get(); */ public function getAllAsArray() { - return $this->builder->getAllAsArray(); + $builder = $this->builder; + assert($builder instanceof FilterBuilder); + + return $builder->getAllAsArray(); } /** @@ -121,12 +133,11 @@ public function getAllAsArray() */ protected function ensureAndEncapsulation() { - $parent = $this->getParent(); - if ($this instanceof AndFilterBuilder) { return $this; } + $parent = $this->getParent(); if (($parent instanceof AndFilterBuilder) && !($this instanceof FilterBuilderWithChildren)) { return $parent; } @@ -227,7 +238,7 @@ public function up() * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyEqualsFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andPropertyEquals($property, $value) { @@ -240,7 +251,7 @@ public function andPropertyEquals($property, $value) * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyEqualsFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function orPropertyEquals($property, $value) { @@ -254,7 +265,7 @@ public function orPropertyEquals($property, $value) * @param string $remoteProperty The name of the remote property. * @param bool $remoteIsValue True if the passed remote value is a value, false if it is a property name. * - * @return PropertyEqualsFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andRemotePropertyEquals($property, $remoteProperty, $remoteIsValue = false) { @@ -271,7 +282,7 @@ public function andRemotePropertyEquals($property, $remoteProperty, $remoteIsVal * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyGreaterThanFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andPropertyGreaterThan($property, $value) { @@ -285,7 +296,7 @@ public function andPropertyGreaterThan($property, $value) * @param string $remoteProperty The name of the remote property. * @param bool $remoteIsValue True if the passed remote value is a value, false if it is a property name. * - * @return PropertyGreaterThanFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andRemotePropertyGreaterThan($property, $remoteProperty, $remoteIsValue = false) { @@ -302,7 +313,7 @@ public function andRemotePropertyGreaterThan($property, $remoteProperty, $remote * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyLessThanFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andPropertyLessThan($property, $value) { @@ -316,7 +327,7 @@ public function andPropertyLessThan($property, $value) * @param string $remoteProperty The name of the remote property. * @param bool $remoteIsValue True if the passed remote value is a value, false if it is a property name. * - * @return PropertyLessThanFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andRemotePropertyLessThan($property, $remoteProperty, $remoteIsValue = false) { @@ -333,7 +344,7 @@ public function andRemotePropertyLessThan($property, $remoteProperty, $remoteIsV * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyValueInFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andPropertyValueIn($property, $value) { @@ -346,7 +357,7 @@ public function andPropertyValueIn($property, $value) * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyValueInFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andPropertyValueLike($property, $value) { diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php b/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php index 47d364ec5..ca0bd63f5 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php @@ -21,30 +21,38 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; +use ArrayAccess; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use Iterator; +use ReturnTypeWillChange; + +use function count; /** * Handy helper class to generate and manipulate filter arrays containing children. * * This class is intended to be only used as base class of other filters and via the FilterBuilder main class. * + * @implements Iterator + * @implements ArrayAccess + * * @SuppressWarnings(PHPMD.TooManyPublicMethods) We have to keep them as we implement the interfaces. */ -class FilterBuilderWithChildren extends BaseFilterBuilder implements \Iterator, \ArrayAccess +class FilterBuilderWithChildren extends BaseFilterBuilder implements Iterator, ArrayAccess { /** * The operation string. * * @var string */ - protected $operation; + protected $operation = ''; /** * The children within this filter builder. * - * @var BaseFilterBuilder[] + * @var array */ protected $children; @@ -58,18 +66,12 @@ class FilterBuilderWithChildren extends BaseFilterBuilder implements \Iterator, /** * Create a new instance. * - * @param array $children The initial children to absorb. + * @param list $children The initial children to absorb. * * @throws DcGeneralInvalidArgumentException When invalid children have been passed. */ public function __construct($children = []) { - if (!\is_array($children)) { - throw new DcGeneralInvalidArgumentException( - __CLASS__ . ' needs a valid child filter array ' . \gettype($children) . 'given' - ); - } - $this->children = []; foreach ($children as $child) { @@ -79,7 +81,7 @@ public function __construct($children = []) } /** - * {@inheritdoc} + * Clone the object. */ public function __clone() { @@ -98,10 +100,10 @@ public function __clone() * * @throws DcGeneralRuntimeException When the current position is invalid. */ - public function current(): ?BaseFilterBuilder + public function current(): BaseFilterBuilder { if (-1 === $this->index) { - return $this->first(); + $this->first(); } if (!$this->valid()) { @@ -114,8 +116,11 @@ public function current(): ?BaseFilterBuilder /** * Move forward to next element and return it. * - * @return BaseFilterBuilder + * @return BaseFilterBuilder|null + * + * @psalm-suppress ImplementedReturnTypeMismatch */ + #[ReturnTypeWillChange] public function next(): ?BaseFilterBuilder { $this->index++; @@ -126,9 +131,9 @@ public function next(): ?BaseFilterBuilder /** * Return the key of the current element. * - * @return mixed scalar on success, or null on failure. + * @return int scalar on success, or null on failure. */ - public function key(): mixed + public function key(): int { return $this->index; } @@ -140,7 +145,7 @@ public function key(): mixed */ public function valid(): bool { - return ($this->index > -1) && ($this->index < \count($this->children)); + return ($this->index > -1) && ($this->index < count($this->children)); } /** @@ -148,8 +153,11 @@ public function valid(): bool * * This is an alias for {@link FilterBuilderWithChildren::first()} only present for implementing Iterator interface. * - * @return BaseFilterBuilder + * @return BaseFilterBuilder|null + * + * @psalm-suppress ImplementedReturnTypeMismatch */ + #[ReturnTypeWillChange] public function rewind(): ?BaseFilterBuilder { return $this->first(); @@ -158,11 +166,11 @@ public function rewind(): ?BaseFilterBuilder /** * Rewind the Iterator to the first element and return it. * - * @return BaseFilterBuilder + * @return BaseFilterBuilder|null */ public function first() { - $this->index = \count($this->children) ? 0 : (-1); + $this->index = count($this->children) ? 0 : (-1); if ($this->index === -1) { return null; @@ -202,7 +210,10 @@ public function offsetGet($offset): BaseFilterBuilder * @param mixed $value The value to set. * * @return FilterBuilderWithChildren The current builder. + * + * @psalm-suppress ImplementedReturnTypeMismatch */ + #[ReturnTypeWillChange] public function offsetSet($offset, $value): FilterBuilderWithChildren { $this->children[$offset] = $value; @@ -216,7 +227,10 @@ public function offsetSet($offset, $value): FilterBuilderWithChildren * @param int $offset The offset to unset. * * @return FilterBuilderWithChildren The current builder. + * + * @psalm-suppress ImplementedReturnTypeMismatch */ + #[ReturnTypeWillChange] public function offsetUnset($offset): FilterBuilderWithChildren { unset($this->children[$offset]); @@ -269,7 +283,10 @@ public function add($filter) if ($index === -1) { $this->children[] = $filter; - $filter->setBuilder($this->builder)->setParent($this); + $filter->setParent($this); + if (null !== $this->builder) { + $filter->setBuilder($this->builder); + } } return $this; @@ -308,6 +325,7 @@ public static function fromArray($array, $builder) $children[] = FilterBuilder::getBuilderFromArray($child, $builder); } + /** @psalm-suppress UnsafeInstantiation */ return (new static($children))->setBuilder($builder); } diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/OrFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/OrFilterBuilder.php index de427cd77..0a2940f47 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/OrFilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/OrFilterBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,7 +33,7 @@ class OrFilterBuilder extends FilterBuilderWithChildren /** * Create a new instance. * - * @param array $children The initial children to absorb. + * @param list $children The initial children to absorb. * * @throws DcGeneralInvalidArgumentException When invalid children have been passed. */ diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/PropertyValueInFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/PropertyValueInFilterBuilder.php index 4233b42ef..fd3341b18 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/PropertyValueInFilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/PropertyValueInFilterBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -51,7 +52,6 @@ class PropertyValueInFilterBuilder extends BaseFilterBuilder */ public function __construct($property, $values) { - $this->operation = 'IN'; $this->setProperty($property)->setValues($values); } @@ -60,7 +60,7 @@ public function __construct($property, $values) * * @param array $array The initialization array. * - * @return mixed + * @return static * * @throws DcGeneralInvalidArgumentException When an invalid array has been passed. */ @@ -73,6 +73,7 @@ public static function fromArray($array) throw new DcGeneralInvalidArgumentException('Invalid filter array provided ' . \var_export($array, true)); } + /** @psalm-suppress UnsafeInstantiation */ return new static($property, $values); } diff --git a/src/DataDefinition/ModelRelationship/ParentChildCondition.php b/src/DataDefinition/ModelRelationship/ParentChildCondition.php index 0acec1122..72ddd84fc 100644 --- a/src/DataDefinition/ModelRelationship/ParentChildCondition.php +++ b/src/DataDefinition/ModelRelationship/ParentChildCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author Tristan Lins * @author Sven Baumann * @author David Molineus - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -46,43 +47,43 @@ class ParentChildCondition extends AbstractCondition implements ParentChildCondi * * @var array */ - protected $inverseFilter; + protected $inverseFilter = []; /** * The values to use when enforcing a root condition. * * @var array */ - protected $setOn; + protected $setOn = []; /** * The name of the source provider (parent). * * @var string */ - protected $sourceProvider; + protected $sourceProvider = ''; /** * The name of the destination provider (child). * * @var string */ - protected $destinationProvider; + protected $destinationProvider = ''; /** * Local cache property for the needed properties for filtering. * - * @var array + * @var list|null */ - private $neededProperties; + private $neededProperties = null; /** * {@inheritdoc} */ public function setFilterArray($value) { - $this->filter = (array) $value; - unset($this->neededProperties); + $this->filter = $value; + $this->neededProperties = null; return $this; } @@ -100,7 +101,7 @@ public function getFilterArray() */ public function setSetters($value) { - $this->setOn = (array) $value; + $this->setOn = $value; return $this; } @@ -118,12 +119,8 @@ public function getSetters() */ public function setInverseFilterArray($value) { - if (empty($value)) { - $this->inverseFilter = null; - return $this; - } + $this->inverseFilter = $value; - $this->inverseFilter = (array) $value; return $this; } @@ -132,7 +129,7 @@ public function setInverseFilterArray($value) */ public function getInverseFilterArray() { - return ($this->inverseFilter ?? []); + return $this->inverseFilter; } /** @@ -140,7 +137,7 @@ public function getInverseFilterArray() */ public function setSourceName($value) { - $this->sourceProvider = (string) $value; + $this->sourceProvider = $value; return $this; } @@ -158,7 +155,7 @@ public function getSourceName() */ public function setDestinationName($value) { - $this->destinationProvider = (string) $value; + $this->destinationProvider = $value; return $this; } @@ -219,10 +216,6 @@ public function parseFilter($filter, $model) */ public function getFilter($parent) { - if (!$parent) { - throw new DcGeneralInvalidArgumentException('No parent model passed.'); - } - $result = []; foreach ($this->getFilterArray() as $child) { $result[] = $this->parseFilter($child, $parent); @@ -234,7 +227,7 @@ public function getFilter($parent) /** * Check if the passed value is a valid setter. * - * @param array $setter The setter. + * @param mixed $setter The setter. * * @return bool */ @@ -257,7 +250,7 @@ public function applyTo($objParent, $objChild) $setters = $this->getSetters(); - if (empty($setters) || !\is_array($setters)) { + if (empty($setters)) { throw new DcGeneralRuntimeException( \sprintf( 'No relationship setter defined from %s to %s.', @@ -300,7 +293,7 @@ public function copyFrom($sourceModel, $destinationModel) $setters = $this->getSetters(); - if (empty($setters) || !\is_array($setters)) { + if (empty($setters)) { throw new DcGeneralRuntimeException( \sprintf( 'No relationship setter defined from %s to %s.', @@ -372,7 +365,7 @@ public function getInverseFilterFor($child) * @param array $rule The rule to prepare. * @param ModelInterface $child The child to be checked. * - * @return array. + * @return array */ protected function prepareRule($rule, $child) { @@ -436,28 +429,31 @@ public function matches($objParent, $objChild) * * @param array $rule The filter rule from which the properties shall be extracted from. * - * @return array + * @return list * * @throws \RuntimeException When an unexpected filter rule is encountered. */ private function extractNeededProperties($rule) { if (\in_array($rule['operation'], ['AND', 'OR'])) { - $properties = []; - foreach ($rule['children'] as $childRule) { + $properties = [[]]; + foreach ($rule['children'] ?? [] as $childRule) { $properties[] = $this->extractNeededProperties($childRule); } - return $properties; + + return \array_merge(...$properties); } // Local is child property name. if (isset($rule['local'])) { - return $rule['local']; + /** @var array{local: string} $rule */ + return [$rule['local']]; } // Remote is parent property name. if (isset($rule['property'])) { - return $rule['property']; + /** @var array{property: string} $rule */ + return [$rule['property']]; } throw new \RuntimeException('Unexpected filter rule ' . \var_export($rule, true)); @@ -468,7 +464,7 @@ private function extractNeededProperties($rule) */ public function neededProperties() { - if (!isset($this->neededProperties)) { + if (null === $this->neededProperties) { $this->neededProperties = $this->extractNeededProperties( [ 'operation' => 'AND', diff --git a/src/DataDefinition/ModelRelationship/ParentChildConditionInterface.php b/src/DataDefinition/ModelRelationship/ParentChildConditionInterface.php index d3d485389..62c07fc03 100644 --- a/src/DataDefinition/ModelRelationship/ParentChildConditionInterface.php +++ b/src/DataDefinition/ModelRelationship/ParentChildConditionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -79,11 +80,11 @@ public function getInverseFilterArray(); /** * Get the condition as filter related to the given model. * - * @param ModelInterface $objParent The model that shall get used as parent. + * @param ModelInterface $parent The model that shall get used as parent. * * @return array */ - public function getFilter($objParent); + public function getFilter($parent); /** * Set the name of the source provider. @@ -142,12 +143,12 @@ public function copyFrom($sourceModel, $destinationModel); * * This allows to look up the parent of a child model. * - * @param ModelInterface $objChild The model that shall get used as child and for which the parent filter shall get - * retrieved. + * @param ModelInterface $child The model that shall get used as child and for which the parent filter shall get + * retrieved. * * @return array|null */ - public function getInverseFilterFor($objChild); + public function getInverseFilterFor($child); /** * Test if the given parent is indeed a parent of the given child object for this condition. @@ -162,7 +163,7 @@ public function matches($objParent, $objChild); /** * Return the names of the needed properties for filtering. * - * @return array + * @return list */ public function neededProperties(); } diff --git a/src/DataDefinition/ModelRelationship/RootCondition.php b/src/DataDefinition/ModelRelationship/RootCondition.php index 46fe84594..1e8df211f 100644 --- a/src/DataDefinition/ModelRelationship/RootCondition.php +++ b/src/DataDefinition/ModelRelationship/RootCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -34,7 +35,7 @@ class RootCondition extends AbstractCondition implements RootConditionInterface * * @var array */ - protected $filter; + protected $filter = []; /** * The setter information to use when a model shall get marked as root item. @@ -48,7 +49,7 @@ class RootCondition extends AbstractCondition implements RootConditionInterface * * @var string */ - protected $sourceProvider; + protected $sourceProvider = ''; /** * {@inheritdoc} @@ -140,17 +141,17 @@ public function applyTo($model) /** * {@inheritdoc} */ - public function matches($model) + public function matches($objModel) { try { - $this->guardProviderName($model); + $this->guardProviderName($objModel); } catch (\InvalidArgumentException $exception) { return false; } if ($this->getFilterArray()) { return static::checkCondition( - $model, + $objModel, [ 'operation' => 'AND', 'children' => $this->getFilterArray() diff --git a/src/DataDefinition/ModelRelationship/RootConditionInterface.php b/src/DataDefinition/ModelRelationship/RootConditionInterface.php index 1aedace32..67c07fc2b 100644 --- a/src/DataDefinition/ModelRelationship/RootConditionInterface.php +++ b/src/DataDefinition/ModelRelationship/RootConditionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,7 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -78,11 +78,11 @@ public function getSourceName(); /** * Apply the root condition to a model. * - * @param ModelInterface $objModel The model that shall become a root model. + * @param ModelInterface $model The model that shall become a root model. * * @return RootConditionInterface */ - public function applyTo($objModel); + public function applyTo($model); /** * Test if the given model is indeed a root object according to this condition. diff --git a/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php b/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php index 28509af2b..338eca0dc 100644 --- a/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,6 +31,9 @@ /** * This event is emitted when an condition is added by the palette builder. + * + * @template TCondition of PaletteConditionInterface|PropertyConditionInterface + * @template TTarget of PaletteInterface|PropertyInterface */ class AddConditionEvent extends BuilderEvent { @@ -38,23 +42,23 @@ class AddConditionEvent extends BuilderEvent /** * The condition that is being added. * - * @var PaletteConditionInterface|PropertyConditionInterface + * @var TCondition */ protected $condition; /** * The target to which the condition is being added. * - * @var PaletteInterface|PropertyInterface + * @var TTarget */ protected $target; /** * Create a new instance. * - * @param PaletteConditionInterface|PropertyConditionInterface $condition The condition being added. - * @param PaletteInterface|PropertyInterface $target The target property or palette. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param TCondition $condition The condition being added. + * @param TTarget $target The target property or palette. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($condition, $target, PaletteBuilder $paletteBuilder) { @@ -66,7 +70,7 @@ public function __construct($condition, $target, PaletteBuilder $paletteBuilder) /** * Set the condition. * - * @param PaletteConditionInterface|PropertyConditionInterface $condition The condition. + * @param TCondition $condition The condition. * * @return AddConditionEvent * @@ -91,7 +95,7 @@ public function setCondition($condition) /** * Retrieve the condition. * - * @return PaletteConditionInterface|PropertyConditionInterface + * @return TCondition */ public function getCondition() { @@ -101,7 +105,7 @@ public function getCondition() /** * Set the target. * - * @param PaletteInterface|PropertyInterface $target The target property or palette. + * @param TTarget $target The target property or palette. * * @return AddConditionEvent * @@ -120,7 +124,7 @@ public function setTarget($target) /** * Retrieve the target. * - * @return PaletteInterface|PropertyInterface + * @return TTarget */ public function getTarget() { diff --git a/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php b/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php index de13da7e2..bbaee38a9 100644 --- a/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php @@ -32,6 +32,9 @@ */ abstract class BuilderEvent extends AbstractContainerAwareEvent { + /** + * @var string + */ public const NAME = 'dc-general.data-definition.palette.builder.builder'; /** @@ -48,7 +51,7 @@ abstract class BuilderEvent extends AbstractContainerAwareEvent */ public function __construct(PaletteBuilder $paletteBuilder) { - parent::__construct($this->paletteBuilder->getContainer()); + parent::__construct($paletteBuilder->getContainer()); $this->paletteBuilder = $paletteBuilder; } diff --git a/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php b/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php index 0c2b5ef09..a4a4175c9 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,6 +29,8 @@ /** * This event gets emitted when a condition is created. + * + * @template TCondition of PaletteConditionInterface|PropertyConditionInterface */ class CreateConditionEvent extends BuilderEvent { @@ -36,16 +39,16 @@ class CreateConditionEvent extends BuilderEvent /** * The condition being created. * - * @var PaletteConditionInterface|PropertyConditionInterface + * @var TCondition */ protected $condition; /** * Create a new instance. * - * @param PaletteConditionInterface|PropertyConditionInterface $condition The condition that has been created. - * @param PaletteBuilder $paletteBuilder The palette builder that created the - * condition. + * @param TCondition $condition The condition that has been created. + * @param PaletteBuilder $paletteBuilder The palette builder that created the + * condition. */ public function __construct($condition, PaletteBuilder $paletteBuilder) { @@ -57,7 +60,7 @@ public function __construct($condition, PaletteBuilder $paletteBuilder) /** * Set the condition. * - * @param PaletteConditionInterface|PropertyConditionInterface $condition The condition to use. + * @param TCondition $condition The condition to use. * * @return CreateConditionEvent * @@ -83,7 +86,7 @@ public function setCondition($condition) /** * Retrieve the condition. * - * @return PaletteConditionInterface|PropertyConditionInterface + * @return TCondition */ public function getCondition() { diff --git a/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php b/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php index 08b47943c..275ba3fc6 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\DefaultPaletteCondition; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; /** * This event gets emitted when a condition for the default palette is created. @@ -34,17 +35,17 @@ class CreateDefaultPaletteConditionEvent extends BuilderEvent /** * The default palette condition. * - * @var DefaultPaletteCondition + * @var PaletteConditionInterface */ protected $paletteCondition; /** * Create a new instance. * - * @param DefaultPaletteCondition $paletteCondition The condition that has been created. - * @param PaletteBuilder $paletteBuilder The palette builder creating the condition. + * @param PaletteConditionInterface $paletteCondition The condition that has been created. + * @param PaletteBuilder $paletteBuilder The palette builder creating the condition. */ - public function __construct(DefaultPaletteCondition $paletteCondition, PaletteBuilder $paletteBuilder) + public function __construct(PaletteConditionInterface $paletteCondition, PaletteBuilder $paletteBuilder) { $this->setDefaultPaletteCondition($paletteCondition); parent::__construct($paletteBuilder); @@ -53,11 +54,11 @@ public function __construct(DefaultPaletteCondition $paletteCondition, PaletteBu /** * Set the condition. * - * @param DefaultPaletteCondition $paletteCondition The condition. + * @param PaletteConditionInterface $paletteCondition The condition. * * @return CreateDefaultPaletteConditionEvent */ - public function setDefaultPaletteCondition(DefaultPaletteCondition $paletteCondition) + public function setDefaultPaletteCondition(PaletteConditionInterface $paletteCondition) { $this->paletteCondition = $paletteCondition; @@ -67,7 +68,7 @@ public function setDefaultPaletteCondition(DefaultPaletteCondition $paletteCondi /** * Retrieve the condition. * - * @return DefaultPaletteCondition + * @return PaletteConditionInterface */ public function getDefaultPaletteCondition() { diff --git a/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php b/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php index 0626ff950..5b50a4f3f 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,15 +14,17 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionChainInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionChain; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; /** * This event gets emitted when a palette condition chain is created. @@ -34,18 +36,20 @@ class CreatePaletteConditionChainEvent extends BuilderEvent /** * The palette condition chain being created. * - * @var PaletteConditionChain + * @var ConditionChainInterface&PaletteConditionInterface */ protected $conditionChain; /** * Create a new instance. * - * @param PaletteConditionChain $conditionChain The palette condition chain. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param ConditionChainInterface&PaletteConditionInterface $conditionChain The palette condition chain. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ - public function __construct(PaletteConditionChain $conditionChain, PaletteBuilder $paletteBuilder) - { + public function __construct( + ConditionChainInterface&PaletteConditionInterface $conditionChain, + PaletteBuilder $paletteBuilder + ) { $this->setPaletteConditionChain($conditionChain); parent::__construct($paletteBuilder); } @@ -53,11 +57,11 @@ public function __construct(PaletteConditionChain $conditionChain, PaletteBuilde /** * Set the palette condition chain. * - * @param PaletteConditionChain $conditionChain The condition chain. + * @param ConditionChainInterface&PaletteConditionInterface $conditionChain The condition chain. * * @return CreatePaletteConditionChainEvent */ - public function setPaletteConditionChain(PaletteConditionChain $conditionChain) + public function setPaletteConditionChain(ConditionChainInterface&PaletteConditionInterface $conditionChain) { $this->conditionChain = $conditionChain; @@ -67,7 +71,7 @@ public function setPaletteConditionChain(PaletteConditionChain $conditionChain) /** * Retrieve the palette condition chain. * - * @return PaletteConditionChain + * @return ConditionChainInterface&PaletteConditionInterface */ public function getPaletteConditionChain() { diff --git a/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php b/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php index 2fdfdac5a..41a55041f 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,15 +14,17 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionChainInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionChain; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; /** * This event gets emitted when a property condition chain is created. @@ -34,18 +36,21 @@ class CreatePropertyConditionChainEvent extends BuilderEvent /** * The property condition chain. * - * @var PropertyConditionChain + * @var ConditionChainInterface&PropertyConditionInterface */ protected $conditionChain; /** * Create a new instance. * - * @param PropertyConditionChain $conditionChain The property condition chain that has been created. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param ConditionChainInterface&PropertyConditionInterface $conditionChain The property condition chain that has + * been created. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ - public function __construct(PropertyConditionChain $conditionChain, PaletteBuilder $paletteBuilder) - { + public function __construct( + ConditionChainInterface&PropertyConditionInterface $conditionChain, + PaletteBuilder $paletteBuilder + ) { $this->setPropertyConditionChain($conditionChain); parent::__construct($paletteBuilder); } @@ -53,11 +58,11 @@ public function __construct(PropertyConditionChain $conditionChain, PaletteBuild /** * Set the property condition chain. * - * @param PropertyConditionChain $conditionChain The property condition chain. + * @param ConditionChainInterface&PropertyConditionInterface $conditionChain The property condition chain. * * @return CreatePropertyConditionChainEvent */ - public function setPropertyConditionChain(PropertyConditionChain $conditionChain) + public function setPropertyConditionChain(ConditionChainInterface&PropertyConditionInterface $conditionChain) { $this->conditionChain = $conditionChain; return $this; @@ -66,7 +71,7 @@ public function setPropertyConditionChain(PropertyConditionChain $conditionChain /** * Retrieve the property condition chain. * - * @return PropertyConditionChain + * @return ConditionChainInterface&PropertyConditionInterface */ public function getPropertyConditionChain() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetDefaultPaletteConditionClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetDefaultPaletteConditionClassNameEvent.php index 6291f5d96..2bddd9e3d 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetDefaultPaletteConditionClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetDefaultPaletteConditionClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; /** * This event gets emitted when the class name of the default palette condition is set. @@ -33,15 +35,15 @@ class SetDefaultPaletteConditionClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -52,20 +54,20 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetDefaultPaletteConditionClassNameEvent */ public function setDefaultPaletteConditionClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getDefaultPaletteConditionClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetLegendClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetLegendClassNameEvent.php index b18d3c532..520216da4 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetLegendClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetLegendClassNameEvent.php @@ -22,6 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\LegendInterface; /** * This event gets emitted when a legend class name is set. @@ -33,15 +34,15 @@ class SetLegendClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $legendClassName; /** * Create a new instance. * - * @param string $legendClassName The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $legendClassName The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($legendClassName, PaletteBuilder $paletteBuilder) { @@ -52,13 +53,13 @@ public function __construct($legendClassName, PaletteBuilder $paletteBuilder) /** * Set the legend class name. * - * @param string $legendClassName The class name. + * @param class-string $legendClassName The class name. * * @return SetLegendClassNameEvent */ public function setLegendClassName($legendClassName) { - $this->legendClassName = (string) $legendClassName; + $this->legendClassName = $legendClassName; return $this; } @@ -66,7 +67,7 @@ public function setLegendClassName($legendClassName) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getLegendClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPaletteClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPaletteClassNameEvent.php index 01fd54dde..26caf6aa6 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPaletteClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPaletteClassNameEvent.php @@ -22,6 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PaletteInterface; /** * This event gets emitted when a palette class name is set. @@ -33,15 +34,15 @@ class SetPaletteClassNameEvent extends BuilderEvent /** * The palette class name. * - * @var string + * @var class-string */ protected $paletteClassName; /** * Create a new instance. * - * @param string $paletteClassName The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $paletteClassName The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($paletteClassName, PaletteBuilder $paletteBuilder) { @@ -52,13 +53,13 @@ public function __construct($paletteClassName, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $paletteClassName The class name. + * @param class-string $paletteClassName The class name. * * @return SetPaletteClassNameEvent */ public function setPaletteClassName($paletteClassName) { - $this->paletteClassName = (string) $paletteClassName; + $this->paletteClassName = $paletteClassName; return $this; } @@ -66,7 +67,7 @@ public function setPaletteClassName($paletteClassName) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPaletteClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPaletteCollectionClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPaletteCollectionClassNameEvent.php index 9fe476feb..ddc9e6cf2 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPaletteCollectionClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPaletteCollectionClassNameEvent.php @@ -22,6 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PaletteCollectionInterface; /** * This event gets emitted when a palette collection class name is set. @@ -33,15 +34,15 @@ class SetPaletteCollectionClassNameEvent extends BuilderEvent /** * The palette collection class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -52,13 +53,13 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetPaletteCollectionClassNameEvent */ public function setPaletteCollectionClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } @@ -66,7 +67,7 @@ public function setPaletteCollectionClassName($className) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPaletteCollectionClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPaletteConditionChainClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPaletteConditionChainClassNameEvent.php index f8afede76..7fa6c02cf 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPaletteConditionChainClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPaletteConditionChainClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,17 +14,22 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionChainInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; /** * This event gets emitted when a palette condition chain class name is set. + * + * @psalm-type TConditionInterface=ConditionChainInterface&PaletteConditionInterface */ class SetPaletteConditionChainClassNameEvent extends BuilderEvent { @@ -33,15 +38,15 @@ class SetPaletteConditionChainClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -52,20 +57,20 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetPaletteConditionChainClassNameEvent */ public function setPaletteConditionChainClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPaletteConditionChainClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPalettePropertyValueConditionClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPalettePropertyValueConditionClassNameEvent.php index 46b92d1a8..92b0c962e 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPalettePropertyValueConditionClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPalettePropertyValueConditionClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; /** * This event gets emitted when a palette property value condition class name is set. @@ -35,15 +37,15 @@ class SetPalettePropertyValueConditionClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -54,13 +56,13 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetPalettePropertyValueConditionClassNameEvent */ public function setPalettePropertyValueConditionClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } @@ -68,7 +70,7 @@ public function setPalettePropertyValueConditionClassName($className) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPalettePropertyValueConditionClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPropertyClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPropertyClassNameEvent.php index 1cdaf7bb1..5c4e83a01 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPropertyClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPropertyClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface; /** * This event gets emitted when a property class name is set. @@ -33,15 +35,15 @@ class SetPropertyClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $propertyClassName; /** * Create a new instance. * - * @param string $propertyClassName The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $propertyClassName The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($propertyClassName, PaletteBuilder $paletteBuilder) { @@ -52,13 +54,13 @@ public function __construct($propertyClassName, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $propertyClassName The class name. + * @param class-string $propertyClassName The class name. * * @return SetPropertyClassNameEvent */ public function setPropertyClassName($propertyClassName) { - $this->propertyClassName = (string) $propertyClassName; + $this->propertyClassName = $propertyClassName; return $this; } @@ -66,7 +68,7 @@ public function setPropertyClassName($propertyClassName) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPropertyClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPropertyConditionChainClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPropertyConditionChainClassNameEvent.php index 35e41aad4..237c086ef 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPropertyConditionChainClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPropertyConditionChainClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,17 +14,23 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionChainInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; /** * This event gets emitted when a property condition chain class name is set. + * + * @psalm-type TConditionInterface=ConditionChainInterface&PropertyConditionInterface + * */ class SetPropertyConditionChainClassNameEvent extends BuilderEvent { @@ -33,15 +39,15 @@ class SetPropertyConditionChainClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -52,13 +58,13 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetPropertyConditionChainClassNameEvent */ public function setPalettePropertyConditionChainClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } @@ -66,7 +72,7 @@ public function setPalettePropertyConditionChainClassName($className) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPalettePropertyConditionChainClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPropertyValueConditionClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPropertyValueConditionClassNameEvent.php index b5cdb3593..59a49aba2 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPropertyValueConditionClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPropertyValueConditionClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; /** * This event gets emitted when a property value condition class name is set. @@ -33,15 +35,15 @@ class SetPropertyValueConditionClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -52,20 +54,20 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetPropertyValueConditionClassNameEvent */ public function setPropertyValueConditionClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPropertyValueConditionClassName() { diff --git a/src/DataDefinition/Palette/Builder/PaletteBuilder.php b/src/DataDefinition/Palette/Builder/PaletteBuilder.php index e80c921f8..df1fd1382 100644 --- a/src/DataDefinition/Palette/Builder/PaletteBuilder.php +++ b/src/DataDefinition/Palette/Builder/PaletteBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Tristan Lins * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -75,10 +76,20 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ReflectionClass; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +use function array_shift; +use function count; +use function func_get_args; +use function is_object; /** * The palette builder is used to build palette collections, palettes, legends, properties and conditions. * + * @psalm-type TPaletteConditionChainInterface=ConditionChainInterface&PaletteConditionInterface + * @psalm-type TPropertyConditionChainInterface=ConditionChainInterface&PropertyConditionInterface + * * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.TooManyPublicMethods) @@ -108,126 +119,126 @@ class PaletteBuilder /** * The class name of the class to use for palette collections. * - * @var string + * @var class-string */ protected $paletteCollectionClassName = PaletteCollection::class; /** * The class to use for palette collections. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $paletteCollectionClass; /** * The class name of the class to use for palettes. * - * @var string + * @var class-string */ protected $paletteClassName = Palette::class; /** * The class to use for palettes. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $paletteClass; /** * The class name of the class to use for palette legends. * - * @var string + * @var class-string */ protected $legendClassName = Legend::class; /** * The class to use for palette legends. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $legendClass; /** * The class name of the class to use for palette properties. * - * @var string + * @var class-string */ protected $propertyClassName = Property::class; /** * The class to use for palette properties. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $propertyClass; /** * The class name of the class to use for palette condition chains. * - * @var string + * @var class-string */ protected $paletteConditionChainClassName = PaletteConditionChain::class; /** - * The the class to use for palette condition chains. + * The class to use for palette condition chains. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $paletteConditionChainClass; /** * The class name of the class to use for palette conditions. * - * @var string + * @var class-string */ protected $defaultPaletteConditionClassName = DefaultPaletteCondition::class; /** - * The the class to use for palette conditions. + * The class to use for palette conditions. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $defaultPaletteConditionClass; /** * The class name of the class to use for property value conditions. * - * @var string + * @var class-string */ protected $palettePropertyValueConditionClassName = PropertyValueCondition::class; /** * The class to use for property value conditions. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $palettePropertyValueConditionClass; /** * The class name of the class to use for property condition chains. * - * @var string + * @var class-string */ protected $propertyConditionChainClassName = PropertyConditionChain::class; /** * The class to use for property condition chains. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $propertyConditionChainClass; /** * The class name of the class to use for property value conditions. * - * @var string + * @var class-string */ protected $propertyValueConditionClassName = PropertyPropertyValueCondition::class; /** - * The the class to use for property value conditions. + * The class to use for property value conditions. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $propertyValueConditionClass; @@ -255,14 +266,14 @@ class PaletteBuilder /** * The property currently working on. * - * @var PropertyInterface|null + * @var list|PropertyInterface|null */ protected $property; /** * The condition currently working on. * - * @var PropertyConditionInterface|PaletteConditionInterface|ConditionChainInterface|null + * @var PropertyConditionInterface|PaletteConditionInterface|null */ protected $condition; @@ -271,7 +282,7 @@ class PaletteBuilder * * @param ContainerInterface $container The data definition container for which the palettes shall get built. * - * @return PaletteBuilder + * @return self */ public static function create(ContainerInterface $container) { @@ -287,17 +298,17 @@ public function __construct(ContainerInterface $container) { $this->container = $container; - $this->paletteCollectionClass = new \ReflectionClass($this->paletteCollectionClassName); - $this->paletteClass = new \ReflectionClass($this->paletteClassName); - $this->legendClass = new \ReflectionClass($this->legendClassName); - $this->propertyClass = new \ReflectionClass($this->propertyClassName); + $this->paletteCollectionClass = new ReflectionClass($this->paletteCollectionClassName); + $this->paletteClass = new ReflectionClass($this->paletteClassName); + $this->legendClass = new ReflectionClass($this->legendClassName); + $this->propertyClass = new ReflectionClass($this->propertyClassName); - $this->paletteConditionChainClass = new \ReflectionClass($this->paletteConditionChainClassName); - $this->defaultPaletteConditionClass = new \ReflectionClass($this->defaultPaletteConditionClassName); - $this->palettePropertyValueConditionClass = new \ReflectionClass($this->palettePropertyValueConditionClassName); + $this->paletteConditionChainClass = new ReflectionClass($this->paletteConditionChainClassName); + $this->defaultPaletteConditionClass = new ReflectionClass($this->defaultPaletteConditionClassName); + $this->palettePropertyValueConditionClass = new ReflectionClass($this->palettePropertyValueConditionClassName); - $this->propertyConditionChainClass = new \ReflectionClass($this->propertyConditionChainClassName); - $this->propertyValueConditionClass = new \ReflectionClass($this->propertyValueConditionClassName); + $this->propertyConditionChainClass = new ReflectionClass($this->propertyConditionChainClassName); + $this->propertyValueConditionClass = new ReflectionClass($this->propertyValueConditionClassName); } /** @@ -313,9 +324,9 @@ public function getContainer() /** * Set the palette collection class name. * - * @param string $paletteCollectionClassName The class name. + * @param class-string $paletteCollectionClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPaletteCollectionClassName($paletteCollectionClassName) { @@ -323,15 +334,16 @@ public function setPaletteCollectionClassName($paletteCollectionClassName) $this->dispatchEvent($event); $paletteCollectionClassName = $event->getPaletteCollectionClassName(); - $this->paletteCollectionClassName = (string) $paletteCollectionClassName; - $this->paletteCollectionClass = new \ReflectionClass($this->paletteCollectionClassName); + $this->paletteCollectionClassName = $paletteCollectionClassName; + $this->paletteCollectionClass = new ReflectionClass($this->paletteCollectionClassName); + return $this; } /** * Return the palette collection class name. * - * @return string + * @return class-string */ public function getPaletteCollectionClassName() { @@ -341,9 +353,9 @@ public function getPaletteCollectionClassName() /** * Set the palette class name. * - * @param string $paletteClassName The class name. + * @param class-string $paletteClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPaletteClassName($paletteClassName) { @@ -351,15 +363,16 @@ public function setPaletteClassName($paletteClassName) $this->dispatchEvent($event); $paletteClassName = $event->getPaletteClassName(); - $this->paletteClassName = (string) $paletteClassName; - $this->paletteClass = new \ReflectionClass($this->paletteClassName); + $this->paletteClassName = $paletteClassName; + $this->paletteClass = new ReflectionClass($this->paletteClassName); + return $this; } /** * Return the palette class name. * - * @return string + * @return class-string */ public function getPaletteClassName() { @@ -369,9 +382,9 @@ public function getPaletteClassName() /** * Set the legend class name. * - * @param string $legendClassName The class name. + * @param class-string $legendClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setLegendClassName($legendClassName) { @@ -379,15 +392,16 @@ public function setLegendClassName($legendClassName) $this->dispatchEvent($event); $legendClassName = $event->getLegendClassName(); - $this->legendClassName = (string) $legendClassName; - $this->legendClass = new \ReflectionClass($this->legendClassName); + $this->legendClassName = $legendClassName; + $this->legendClass = new ReflectionClass($this->legendClassName); + return $this; } /** * Return the legend class name. * - * @return string + * @return class-string */ public function getLegendClassName() { @@ -397,9 +411,9 @@ public function getLegendClassName() /** * Set the property class name. * - * @param string $propertyClassName The class name. + * @param class-string $propertyClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPropertyClassName($propertyClassName) { @@ -407,15 +421,16 @@ public function setPropertyClassName($propertyClassName) $this->dispatchEvent($event); $propertyClassName = $event->getPropertyClassName(); - $this->propertyClassName = (string) $propertyClassName; - $this->propertyClass = new \ReflectionClass($this->propertyClassName); + $this->propertyClassName = $propertyClassName; + $this->propertyClass = new ReflectionClass($this->propertyClassName); + return $this; } /** * Return the property class name. * - * @return string + * @return class-string */ public function getPropertyClassName() { @@ -425,9 +440,9 @@ public function getPropertyClassName() /** * Set the palette condition chain class name. * - * @param string $paletteConditionChainClassName The class name. + * @param class-string $paletteConditionChainClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPaletteConditionChainClassName($paletteConditionChainClassName) { @@ -435,15 +450,16 @@ public function setPaletteConditionChainClassName($paletteConditionChainClassNam $this->dispatchEvent($event); $paletteConditionChainClassName = $event->getPaletteConditionChainClassName(); - $this->paletteConditionChainClassName = (string) $paletteConditionChainClassName; - $this->paletteConditionChainClass = new \ReflectionClass($this->paletteConditionChainClassName); + $this->paletteConditionChainClassName = $paletteConditionChainClassName; + $this->paletteConditionChainClass = new ReflectionClass($this->paletteConditionChainClassName); + return $this; } /** * Return the palette condition chain class name. * - * @return string + * @return class-string */ public function getPaletteConditionChainClassName() { @@ -463,9 +479,9 @@ public function getPaletteCollection() /** * Set the default palette condition class name. * - * @param string $defaultPaletteConditionClassName The class name. + * @param class-string $defaultPaletteConditionClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setDefaultPaletteConditionClassName($defaultPaletteConditionClassName) { @@ -473,15 +489,16 @@ public function setDefaultPaletteConditionClassName($defaultPaletteConditionClas $this->dispatchEvent($event); $defaultPaletteConditionClassName = $event->getDefaultPaletteConditionClassName(); - $this->defaultPaletteConditionClassName = (string) $defaultPaletteConditionClassName; - $this->defaultPaletteConditionClass = new \ReflectionClass($this->defaultPaletteConditionClassName); + $this->defaultPaletteConditionClassName = $defaultPaletteConditionClassName; + $this->defaultPaletteConditionClass = new ReflectionClass($this->defaultPaletteConditionClassName); + return $this; } /** * Return the default palette condition class name. * - * @return string + * @return class-string */ public function getDefaultPaletteConditionClassName() { @@ -491,9 +508,9 @@ public function getDefaultPaletteConditionClassName() /** * Set the palette property value condition class name. * - * @param string $palettePropertyValueConditionClassName The class name. + * @param class-string $palettePropertyValueConditionClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPalettePropertyValueConditionClassName($palettePropertyValueConditionClassName) { @@ -501,17 +518,18 @@ public function setPalettePropertyValueConditionClassName($palettePropertyValueC $this->dispatchEvent($event); $palettePropertyValueConditionClassName = $event->getPalettePropertyValueConditionClassName(); - $this->palettePropertyValueConditionClassName = (string) $palettePropertyValueConditionClassName; - $this->palettePropertyValueConditionClass = new \ReflectionClass( + $this->palettePropertyValueConditionClassName = $palettePropertyValueConditionClassName; + $this->palettePropertyValueConditionClass = new ReflectionClass( $this->palettePropertyValueConditionClassName ); + return $this; } /** * Return the palette property value condition class name. * - * @return string + * @return class-string */ public function getPalettePropertyValueConditionClassName() { @@ -521,9 +539,9 @@ public function getPalettePropertyValueConditionClassName() /** * Set the property condition chain class name. * - * @param string $propertyConditionChainClassName The class name. + * @param class-string $propertyConditionChainClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPropertyConditionChainClassName($propertyConditionChainClassName) { @@ -531,15 +549,16 @@ public function setPropertyConditionChainClassName($propertyConditionChainClassN $this->dispatchEvent($event); $propertyConditionChainClassName = $event->getPalettePropertyConditionChainClassName(); - $this->propertyConditionChainClassName = (string) $propertyConditionChainClassName; - $this->propertyConditionChainClass = new \ReflectionClass($this->propertyConditionChainClassName); + $this->propertyConditionChainClassName = $propertyConditionChainClassName; + $this->propertyConditionChainClass = new ReflectionClass($this->propertyConditionChainClassName); + return $this; } /** * Return the property condition chain class name. * - * @return string + * @return class-string */ public function getPropertyConditionChainClassName() { @@ -549,9 +568,9 @@ public function getPropertyConditionChainClassName() /** * Set the property value condition class name. * - * @param string $propertyValueConditionClassName The class name. + * @param class-string $propertyValueConditionClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPropertyValueConditionClassName($propertyValueConditionClassName) { @@ -559,15 +578,16 @@ public function setPropertyValueConditionClassName($propertyValueConditionClassN $this->dispatchEvent($event); $propertyValueConditionClassName = $event->getPropertyValueConditionClassName(); - $this->propertyValueConditionClassName = (string) $propertyValueConditionClassName; - $this->propertyValueConditionClass = new \ReflectionClass($this->propertyValueConditionClassName); + $this->propertyValueConditionClassName = $propertyValueConditionClassName; + $this->propertyValueConditionClass = new ReflectionClass($this->propertyValueConditionClassName); + return $this; } /** * Return the property value condition class name. * - * @return string + * @return class-string */ public function getPropertyValueConditionClassName() { @@ -597,7 +617,7 @@ public function getLegend() /** * Return the current property object. * - * @return PropertyInterface|null + * @return list|PropertyInterface|null */ public function getProperty() { @@ -607,7 +627,7 @@ public function getProperty() /** * Return the current condition object. * - * @return PaletteConditionInterface|PropertyConditionInterface|null + * @return PropertyConditionInterface|PaletteConditionInterface|null */ public function getCondition() { @@ -619,7 +639,7 @@ public function getCondition() * * @param PaletteCollectionInterface $paletteCollection The palette collection to reuse. * - * @return PaletteBuilder + * @return self */ public function usePaletteCollection(PaletteCollectionInterface $paletteCollection) { @@ -637,7 +657,7 @@ public function usePaletteCollection(PaletteCollectionInterface $paletteCollecti /** * Start a new palette collection. * - * @return PaletteBuilder + * @return self */ public function createPaletteCollection() { @@ -659,7 +679,7 @@ public function createPaletteCollection() * * @param PaletteCollectionInterface $collection Return the final palette collection. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no collection is stored. */ @@ -689,7 +709,7 @@ public function finishPaletteCollection(&$collection = null) * * @param PaletteInterface $palette The palette to reuse. * - * @return PaletteBuilder + * @return self */ public function usePalette(PaletteInterface $palette) { @@ -710,7 +730,7 @@ public function usePalette(PaletteInterface $palette) * @param string|null $name Only for backwards compatibility, We will remove palette names in the future * (deprecated). * - * @return PaletteBuilder + * @return self */ public function createPalette($name = null) { @@ -736,7 +756,7 @@ public function createPalette($name = null) * * @param PaletteInterface $palette Return the final palette. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no palette is stored in the builder. */ @@ -771,7 +791,7 @@ public function finishPalette(&$palette = null) * * @param LegendInterface $legend The legend. * - * @return PaletteBuilder + * @return self */ public function useLegend(LegendInterface $legend) { @@ -791,7 +811,7 @@ public function useLegend(LegendInterface $legend) * * @param string $name Name of the legend. * - * @return PaletteBuilder + * @return self */ public function createLegend($name) { @@ -813,7 +833,7 @@ public function createLegend($name) * * @param LegendInterface $legend Return the final legend. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no legend is stored in the builder. */ @@ -846,7 +866,7 @@ public function finishLegend(&$legend = null) * @param PropertyInterface $property The first property. * @param PropertyInterface $_ Any more subsequent properties to be used. * - * @return PaletteBuilder + * @return self * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.ShortVariable) @@ -858,7 +878,8 @@ public function useProperty($property, $_ = null) $this->finishProperty(); } - $properties = \func_get_args(); + /** @var list $properties */ + $properties = func_get_args(); $this->property = []; foreach ($properties as $property) { @@ -868,8 +889,8 @@ public function useProperty($property, $_ = null) $this->property[] = $property; } - if (1 === \count($this->property)) { - $this->property = \array_shift($this->property); + if (1 === count($this->property)) { + $this->property = array_shift($this->property); } return $this; @@ -881,7 +902,7 @@ public function useProperty($property, $_ = null) * @param string $propertyName The name of the property. * @param PropertyInterface $_ Any more subsequent property names to be used. * - * @return PaletteBuilder + * @return self * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.ShortVariable) @@ -893,7 +914,7 @@ public function createProperty($propertyName, $_ = null) $this->finishProperty(); } - $propertyNames = \func_get_args(); + $propertyNames = func_get_args(); $this->property = []; foreach ($propertyNames as $propertyName) { @@ -906,8 +927,8 @@ public function createProperty($propertyName, $_ = null) $this->property[] = $property; } - if (1 === \count($this->property)) { - $this->property = \array_shift($this->property); + if (1 === count($this->property)) { + $this->property = array_shift($this->property); } return $this; @@ -918,7 +939,7 @@ public function createProperty($propertyName, $_ = null) * * @param PropertyInterface|PropertyInterface[] $property Return the final property or set of properties. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no property is stored in the builder. */ @@ -932,7 +953,7 @@ public function finishProperty(&$property = null) $this->finishCondition(); } - $properties = \is_object($this->property) ? [$this->property] : $this->property; + $properties = is_object($this->property) ? [$this->property] : $this->property; foreach ($properties as $index => $tempProperty) { $event = new FinishPropertyEvent($tempProperty, $this); @@ -954,11 +975,13 @@ public function finishProperty(&$property = null) /** * Create a palette condition chain. * - * @return PaletteBuilder + * @return self + * + * @psalm-assert ConditionChainInterface $this->condition */ protected function createPaletteConditionChain() { - if (!$this->condition instanceof PaletteConditionChain) { + if (!$this->condition instanceof ConditionChainInterface) { $previousCondition = $this->condition; $condition = $this->paletteConditionChainClass->newInstance(); @@ -969,8 +992,12 @@ protected function createPaletteConditionChain() $event = new CreateConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getCondition(); - /** @var ConditionChainInterface $condition */ - $condition->addCondition($previousCondition); + assert($condition instanceof PaletteConditionInterface); + assert($condition instanceof ConditionChainInterface); + + if ($previousCondition) { + $condition->addCondition($previousCondition); + } $this->condition = $condition; } @@ -983,12 +1010,15 @@ protected function createPaletteConditionChain() * * @param string $conjunction The conjunction to use (defaults to AND). * - * @return PaletteBuilder + * @return self + * + * @psalm-assert ConditionChainInterface $this->condition */ - protected function createPropertyConditionChain($conjunction = PropertyConditionChain::AND_CONJUNCTION) + protected function createPropertyConditionChain($conjunction = ConditionChainInterface::AND_CONJUNCTION) { if ( - !($this->condition instanceof PropertyConditionChain) + // FIXME: We need a condition chain interface here to allow proper overriding. + !($this->condition instanceof ConditionChainInterface) || ($conjunction !== $this->condition->getConjunction()) ) { $previousCondition = $this->condition; @@ -1001,8 +1031,9 @@ protected function createPropertyConditionChain($conjunction = PropertyCondition $event = new CreateConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getCondition(); - /** @var ConditionChainInterface $condition */ - $condition->addCondition($previousCondition); + if (null !== $previousCondition) { + $condition->addCondition($previousCondition); + } $this->condition = $condition; } @@ -1013,7 +1044,7 @@ protected function createPropertyConditionChain($conjunction = PropertyCondition /** * Start a new default-palette condition. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no palette or property has been stored. */ @@ -1044,7 +1075,7 @@ public function createDefaultPaletteCondition() /** * Start a new default-palette condition and chain with previous condition. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no palette or property has been stored. */ @@ -1079,7 +1110,7 @@ public function chainDefaultPaletteCondition() * @param mixed $propertyValue The value of the property. * @param bool $strict Flag if the comparison shall be strict (type safe). * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException If neither a palette nor a property is stored in the builder. */ @@ -1098,15 +1129,17 @@ public function createPropertyValueCondition($propertyName, $propertyValue, $str 'Does not know where to create the property-value condition, please create a palette or property first' ); } - + // FIXME: we need an interface for PropertyValueCondition + assert( + ($condition instanceof PropertyValueCondition) + || ($condition instanceof PropertyPropertyValueCondition) + ); $condition->setPropertyName($propertyName); $condition->setPropertyValue($propertyValue); $condition->setStrict($strict); - $event = new CreatePropertyValueConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getPropertyValueCondition(); - $event = new CreateConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getCondition(); @@ -1124,7 +1157,7 @@ public function createPropertyValueCondition($propertyName, $propertyValue, $str * @param bool $strict Flag if the comparison shall be strict (type safe). * @param string $conjunction The conjunction. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException If neither a palette nor a property is stored in the builder. */ @@ -1146,14 +1179,17 @@ public function chainPropertyValueCondition( ); } + // FIXME: we need an interface for PropertyValueCondition + assert( + ($condition instanceof PropertyValueCondition) + || ($condition instanceof PropertyPropertyValueCondition) + ); $condition->setPropertyName($propertyName); $condition->setPropertyValue($propertyValue); $condition->setStrict($strict); - $event = new CreatePropertyValueConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getPropertyValueCondition(); - $event = new CreateConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getCondition(); @@ -1168,7 +1204,7 @@ public function chainPropertyValueCondition( * * @param PropertyConditionInterface|PaletteConditionInterface $condition Return the final condition. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException If no condition is stored in the builder. */ @@ -1230,17 +1266,19 @@ protected function addPaletteCondition(PaletteConditionInterface $condition) * @return void * * @throws DcGeneralRuntimeException If the the property is missing. + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function addPropertyCondition(PropertyConditionInterface $condition, $scope = self::VISIBLE) { - if (!$this->property) { + if (null === $this->property || [] === $this->property) { throw new DcGeneralRuntimeException('Property is missing, please create a property first'); } - $properties = \is_object($this->property) ? [$this->property] : $this->property; + $properties = ($this->property instanceof PropertyInterface) ? [$this->property] : $this->property; foreach ($properties as $property) { - /** @var PropertyInterface $property */ + /** @var AddConditionEvent $event */ $event = new AddConditionEvent($condition, $property, $this); $this->dispatchEvent($event); $condition = $event->getCondition(); @@ -1277,7 +1315,7 @@ protected function addPropertyCondition(PropertyConditionInterface $condition, $ * @param PaletteConditionInterface|PropertyConditionInterface $condition The condition to add. * @param string $scope The scope. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralInvalidArgumentException When an unknown condition type is passed. */ @@ -1293,8 +1331,9 @@ public function addCondition($condition, $scope = self::VISIBLE) return $this; } - $type = \is_object($condition) ? \get_class($condition) : \gettype($condition); - throw new DcGeneralInvalidArgumentException('Cannot handle condition of type [' . $type . ']'); + throw new DcGeneralInvalidArgumentException( + 'Cannot handle condition of type [' . \get_debug_type($condition) . ']' + ); } /** @@ -1309,6 +1348,8 @@ public function addCondition($condition, $scope = self::VISIBLE) protected function dispatchEvent(BuilderEvent $event): void { $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); + $dispatcher->dispatch($event, $event::NAME); } } diff --git a/src/DataDefinition/Palette/Condition/Palette/AbstractBoolPaletteCondition.php b/src/DataDefinition/Palette/Condition/Palette/AbstractBoolPaletteCondition.php index 989f93cd8..9726282be 100644 --- a/src/DataDefinition/Palette/Condition/Palette/AbstractBoolPaletteCondition.php +++ b/src/DataDefinition/Palette/Condition/Palette/AbstractBoolPaletteCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -49,8 +50,8 @@ abstract class AbstractBoolPaletteCondition extends AbstractWeightAwarePaletteCo */ public function __construct($propertyName = '', $strict = false, $weight = 1) { - $this->propertyName = (string) $propertyName; - $this->strict = (bool) $strict; + $this->propertyName = $propertyName; + $this->strict = $strict; $this->setWeight($weight); } @@ -63,7 +64,7 @@ public function __construct($propertyName = '', $strict = false, $weight = 1) */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; return $this; } @@ -87,7 +88,7 @@ public function getPropertyName() */ public function setStrict($strict) { - $this->strict = (bool) $strict; + $this->strict = $strict; return $this; } diff --git a/src/DataDefinition/Palette/Condition/Palette/PaletteConditionChain.php b/src/DataDefinition/Palette/Condition/Palette/PaletteConditionChain.php index d453088bd..51e4866af 100644 --- a/src/DataDefinition/Palette/Condition/Palette/PaletteConditionChain.php +++ b/src/DataDefinition/Palette/Condition/Palette/PaletteConditionChain.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -34,7 +35,7 @@ class PaletteConditionChain extends AbstractConditionChain implements PaletteCon /** * {@inheritdoc} * - * @throws DcGeneralRuntimeException When an condition that does not implement PaletteConditionInterface + * @throws DcGeneralRuntimeException When a condition that does not implement PaletteConditionInterface * is encountered. */ public function getMatchCount(ModelInterface $model = null, PropertyValueBag $input = null) @@ -49,6 +50,9 @@ public function getMatchCount(ModelInterface $model = null, PropertyValueBag $in $conditionCount = $condition->getMatchCount($model, $input); if (false !== $conditionCount) { + if (false === $totalCount) { + $totalCount = 0; + } $totalCount += $conditionCount; } elseif (static::AND_CONJUNCTION === $this->conjunction) { return false; diff --git a/src/DataDefinition/Palette/Condition/Palette/PaletteConditionInterface.php b/src/DataDefinition/Palette/Condition/Palette/PaletteConditionInterface.php index 2af7520fe..1e57e5e37 100644 --- a/src/DataDefinition/Palette/Condition/Palette/PaletteConditionInterface.php +++ b/src/DataDefinition/Palette/Condition/Palette/PaletteConditionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -40,10 +41,10 @@ interface PaletteConditionInterface extends ConditionInterface * When the condition does match, it must return a numeric value, the value may be negative or positive and even * zero. * - * @param ModelInterface|null $model If given, selectors will be evaluated depending on the model. - * @param PropertyValueBag $input If given, selectors will be evaluated depending on the input data. + * @param ModelInterface|null $model If given, selectors will be evaluated depending on the model. + * @param PropertyValueBag|null $input If given, selectors will be evaluated depending on the input data. * - * @return bool|int + * @return false|int */ public function getMatchCount(ModelInterface $model = null, PropertyValueBag $input = null); diff --git a/src/DataDefinition/Palette/Condition/Palette/PropertyValueCondition.php b/src/DataDefinition/Palette/Condition/Palette/PropertyValueCondition.php index c29f43205..c6c2843aa 100644 --- a/src/DataDefinition/Palette/Condition/Palette/PropertyValueCondition.php +++ b/src/DataDefinition/Palette/Condition/Palette/PropertyValueCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -60,9 +61,9 @@ class PropertyValueCondition extends AbstractWeightAwarePaletteCondition */ public function __construct($propertyName = '', $propertyValue = null, $strict = false, $weight = 1) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; $this->propertyValue = $propertyValue; - $this->strict = (bool) $strict; + $this->strict = $strict; $this->setWeight($weight); } @@ -75,7 +76,7 @@ public function __construct($propertyName = '', $propertyValue = null, $strict = */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; return $this; } @@ -121,7 +122,7 @@ public function getPropertyValue() */ public function setStrict($strict) { - $this->strict = (bool) $strict; + $this->strict = $strict; return $this; } diff --git a/src/DataDefinition/Palette/Condition/Property/BooleanCondition.php b/src/DataDefinition/Palette/Condition/Property/BooleanCondition.php index b322c32b1..889dd176d 100644 --- a/src/DataDefinition/Palette/Condition/Property/BooleanCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/BooleanCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -45,7 +46,7 @@ class BooleanCondition implements PropertyConditionInterface */ public function __construct($value) { - $this->value = (bool) $value; + $this->value = $value; } /** @@ -57,7 +58,7 @@ public function __construct($value) */ public function setValue($value) { - $this->value = (bool) $value; + $this->value = $value; return $this; } diff --git a/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php b/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php index 14798ea71..8a04ce575 100644 --- a/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Tristan Lins * @author Sven Baumann * @author David Molineus - * @copyright 2013-2022 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -54,6 +55,8 @@ public function __construct($propertyCondition) * {@inheritdoc} * * @SuppressWarnings(PHPMD.DevelopmentCodeFragment) + * + * @psalm-suppress ForbiddenCode - We explicitly allow var_dump() here for debugging purposes. */ public function match( ModelInterface $model = null, @@ -63,7 +66,7 @@ public function match( ) { $result = $this->propertyCondition->match($model, $input, $property, $legend); - // @codingStandardsIgnoreStart - We explicitely allow var_dump() here for debugging purposes. + // @codingStandardsIgnoreStart - We explicitly allow var_dump() here for debugging purposes. echo '
$condition: 
'; \var_dump($this->propertyCondition); echo '
$model: 
'; diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyConditionInterface.php b/src/DataDefinition/Palette/Condition/Property/PropertyConditionInterface.php index 204e712f6..e9ff3aadc 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyConditionInterface.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyConditionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,14 +36,14 @@ interface PropertyConditionInterface extends ConditionInterface /** * Check if the condition match. * - * @param ModelInterface|null $model If given, subpalettes will be evaluated depending on the model. - * If no model is given, all properties will be returned, including subpalette - * properties. - * @param PropertyValueBag $input If given, subpalettes will be evaluated depending on the input data. - * If no model and no input data is given, all properties will be returned, - * including subpalette properties. - * @param PropertyInterface $property The defined property. - * @param LegendInterface $legend The legend the property is assigned to. + * @param ModelInterface|null $model If given, subpalettes will be evaluated depending on the model. + * If no model is given, all properties will be returned, including + * subpalette properties. + * @param PropertyValueBag|null $input If given, subpalettes will be evaluated depending on the input data. + * If no model and no input data is given, all properties will be returned, + * including subpalette properties. + * @param PropertyInterface|null $property The defined property. + * @param LegendInterface|null $legend The legend the property is assigned to. * * @return bool */ diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyEditableCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyEditableCondition.php index e8b030c3b..ecda816c5 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyEditableCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyEditableCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -45,7 +46,7 @@ class PropertyEditableCondition implements PropertyConditionInterface */ public function __construct($propertyName = '') { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; } /** @@ -57,7 +58,7 @@ public function __construct($propertyName = '') */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; return $this; } @@ -84,8 +85,8 @@ public function match( return false; } - if ($legend->getPalette()) { - return $legend->getPalette()->getProperty($this->propertyName)->isEditable($model, $input, $legend); + if (null !== ($palette = $legend->getPalette())) { + return $palette->getProperty($this->propertyName)->isEditable($model, $input, $legend); } return $legend->getProperty($this->propertyName)->isEditable($model, $input, $legend); diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyFalseCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyFalseCondition.php index 7dfb9361c..9bee678b0 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyFalseCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyFalseCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,81 +32,7 @@ */ class PropertyFalseCondition implements PropertyConditionInterface { - /** - * The property name. - * - * @var string - */ - protected $propertyName; - - /** - * Use strict compare mode. - * - * @var bool - */ - protected $strict; - - /** - * Create a new instance. - * - * @param string $propertyName The name of the property. - * @param bool $strict Flag if the comparison shall be strict (type safe). - */ - public function __construct($propertyName, $strict = false) - { - $this->propertyName = (string) $propertyName; - $this->strict = (bool) $strict; - } - - /** - * Set the property name. - * - * @param string $propertyName The property name. - * - * @return PropertyFalseCondition - */ - public function setPropertyName($propertyName) - { - $this->propertyName = (string) $propertyName; - - return $this; - } - - /** - * Retrieve the property name. - * - * @return string - */ - public function getPropertyName() - { - return $this->propertyName; - } - - /** - * Set the flag if the comparison shall be strict (type safe). - * - * @param boolean $strict The flag. - * - * @return PropertyFalseCondition - */ - public function setStrict($strict) - { - $this->strict = (bool) $strict; - - return $this; - } - - /** - * Retrieve the flag if the comparison shall be strict (type safe). - * - * @return boolean - * - * @SuppressWarnings(PHPMD.BooleanGetMethodName) - */ - public function getStrict() - { - return $this->strict; - } + use PropertyBooleanConditionTrait; /** * {@inheritdoc} @@ -128,11 +55,4 @@ public function match( return $this->strict ? ($value === false) : !$value; } - - /** - * {@inheritdoc} - */ - public function __clone() - { - } } diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyTrueCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyTrueCondition.php index 156d8fd56..336166504 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyTrueCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyTrueCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,79 +32,7 @@ */ class PropertyTrueCondition implements PropertyConditionInterface { - /** - * The property name. - * - * @var string - */ - protected $propertyName; - - /** - * Use strict compare mode. - * - * @var bool - */ - protected $strict; - - /** - * Create a new instance. - * - * @param string $propertyName The name of the property. - * @param bool $strict Flag if the comparison shall be strict (type safe). - */ - public function __construct($propertyName = '', $strict = false) - { - $this->propertyName = (string) $propertyName; - $this->strict = (bool) $strict; - } - - /** - * Set the property name. - * - * @param string $propertyName The property name. - * - * @return PropertyTrueCondition - */ - public function setPropertyName($propertyName) - { - $this->propertyName = $propertyName; - return $this; - } - - /** - * Retrieve the property name. - * - * @return string - */ - public function getPropertyName() - { - return $this->propertyName; - } - - /** - * Set the flag if the comparison shall be strict (type safe). - * - * @param boolean $strict The flag. - * - * @return PropertyTrueCondition - */ - public function setStrict($strict) - { - $this->strict = $strict; - return $this; - } - - /** - * Retrieve the flag if the comparison shall be strict (type safe). - * - * @return boolean - * - * @SuppressWarnings(PHPMD.BooleanGetMethodName) - */ - public function getStrict() - { - return $this->strict; - } + use PropertyBooleanConditionTrait; /** * {@inheritdoc} @@ -124,11 +53,4 @@ public function match( return $this->strict ? ($value === true) : (bool) $value; } - - /** - * {@inheritdoc} - */ - public function __clone() - { - } } diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php index e55fb4c04..a37c45dca 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -61,9 +62,9 @@ class PropertyValueCondition implements PropertyConditionInterface */ public function __construct($propertyName = '', $propertyValue = null, $strict = false) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; $this->propertyValue = $propertyValue; - $this->strict = (bool) $strict; + $this->strict = $strict; } /** @@ -75,7 +76,8 @@ public function __construct($propertyName = '', $propertyValue = null, $strict = */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; + return $this; } @@ -99,6 +101,7 @@ public function getPropertyName() public function setPropertyValue($propertyValue) { $this->propertyValue = $propertyValue; + return $this; } @@ -121,7 +124,8 @@ public function getPropertyValue() */ public function setStrict($strict) { - $this->strict = (bool) $strict; + $this->strict = $strict; + return $this; } diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyVisibleCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyVisibleCondition.php index ebcba4dc1..0f0ba0358 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyVisibleCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyVisibleCondition.php @@ -45,7 +45,7 @@ class PropertyVisibleCondition implements PropertyConditionInterface */ public function __construct($propertyName = '') { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; } /** @@ -57,7 +57,7 @@ public function __construct($propertyName = '') */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; return $this; } @@ -84,8 +84,8 @@ public function match( return false; } - if ($legend->getPalette()) { - return $legend->getPalette()->getProperty($this->propertyName)->isVisible($model, $input, $legend); + if (null !== ($palette = $legend->getPalette())) { + return $palette->getProperty($this->propertyName)->isVisible($model, $input, $legend); } return $legend->getProperty($this->propertyName)->isVisible($model, $input, $legend); diff --git a/src/DataDefinition/Palette/Legend.php b/src/DataDefinition/Palette/Legend.php index 2d96ce052..3f4378e06 100644 --- a/src/DataDefinition/Palette/Legend.php +++ b/src/DataDefinition/Palette/Legend.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; @@ -36,7 +38,7 @@ class Legend implements LegendInterface * * @var PaletteInterface|null */ - protected $palette; + protected $palette = null; /** * The name of this legend. @@ -55,7 +57,7 @@ class Legend implements LegendInterface /** * The properties in this legend. * - * @var PropertyInterface[]|array + * @var array */ protected $properties = []; @@ -95,7 +97,7 @@ public function getPalette() */ public function setName($name) { - $this->name = (string) $name; + $this->name = $name; return $this; } @@ -112,7 +114,7 @@ public function getName() */ public function setInitialVisibility($value) { - $this->initiallyVisible = (bool) $value; + $this->initiallyVisible = $value; return $this; } @@ -176,13 +178,15 @@ public function addProperty(PropertyInterface $property, PropertyInterface $befo ); } - $hashes = \array_keys($this->properties); - $position = \array_search($beforeHash, $hashes); + $hashes = \array_keys($this->properties); + if (false === ($position = \array_search($beforeHash, $hashes))) { + $position = null; + } $this->properties = \array_merge( \array_slice($this->properties, 0, $position), [$hash => $property], - \array_slice($this->properties, $position) + \array_slice($this->properties, $position ?? 0) ); return $this; @@ -212,7 +216,8 @@ public function getProperties(?ModelInterface $model = null, ?PropertyValueBagIn foreach ($this->properties as $property) { $condition = $property->getVisibleCondition(); - + // We should have defined the interfaces back in 2013... :/ + assert($input === null || $input instanceof PropertyValueBag); if (!$condition || $condition->match($model, $input, $property, $this)) { $selectedProperties[] = $property; } diff --git a/src/DataDefinition/Palette/Palette.php b/src/DataDefinition/Palette/Palette.php index 3f2f61154..bb5596457 100644 --- a/src/DataDefinition/Palette/Palette.php +++ b/src/DataDefinition/Palette/Palette.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,12 +38,12 @@ class Palette implements PaletteInterface * * @var string */ - protected $name; + protected $name = ''; /** * List of all legends in this palette. * - * @var array|LegendInterface[] + * @var array */ protected $legends = []; @@ -58,7 +59,7 @@ class Palette implements PaletteInterface */ public function setName($name) { - $this->name = (string) $name; + $this->name = $name; return $this; } @@ -220,9 +221,9 @@ public function addLegend(LegendInterface $legend, LegendInterface $before = nul $position = \array_search($beforeHash, $hashes); $this->legends = \array_merge( - \array_slice($this->legends, 0, $position), + \array_slice($this->legends, 0, (int) $position), [$hash => $legend], - \array_slice($this->legends, $position) + \array_slice($this->legends, (int) $position) ); $legend->setPalette($this); diff --git a/src/DataDefinition/Palette/PaletteCollection.php b/src/DataDefinition/Palette/PaletteCollection.php index 9ee1b0800..7227a9e88 100644 --- a/src/DataDefinition/Palette/PaletteCollection.php +++ b/src/DataDefinition/Palette/PaletteCollection.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; @@ -33,7 +35,7 @@ class PaletteCollection implements PaletteCollectionInterface /** * The palettes contained in the collection. * - * @var array|PaletteInterface[] + * @var array */ protected $palettes = []; @@ -117,6 +119,8 @@ public function findPalette(ModelInterface $model = null, PropertyValueBagInterf $condition = $palette->getCondition(); if ($condition) { + // We should have defined the interfaces back in 2013... :/ + assert($input === null || $input instanceof PropertyValueBag); $count = $condition->getMatchCount($model, $input); if (false !== $count) { diff --git a/src/DataDefinition/Palette/PaletteCollectionInterface.php b/src/DataDefinition/Palette/PaletteCollectionInterface.php index cbe4cea74..a499b0a19 100644 --- a/src/DataDefinition/Palette/PaletteCollectionInterface.php +++ b/src/DataDefinition/Palette/PaletteCollectionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,6 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; -use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; /** @@ -33,7 +33,7 @@ interface PaletteCollectionInterface /** * Remove all palettes from this collection. * - * @return PaletteCollectionInterface + * @return self */ public function clearPalettes(); @@ -42,7 +42,7 @@ public function clearPalettes(); * * @param array|PaletteInterface[] $palettes The palettes. * - * @return PaletteCollectionInterface + * @return self */ public function setPalettes(array $palettes); @@ -51,7 +51,7 @@ public function setPalettes(array $palettes); * * @param array|PaletteInterface[] $palettes The palettes. * - * @return PaletteInterface + * @return self */ public function addPalettes(array $palettes); @@ -60,7 +60,7 @@ public function addPalettes(array $palettes); * * @param PaletteInterface $palette The palette. * - * @return PaletteInterface + * @return self */ public function addPalette(PaletteInterface $palette); @@ -69,7 +69,7 @@ public function addPalette(PaletteInterface $palette); * * @param PaletteInterface $palette The palette. * - * @return PaletteInterface + * @return self */ public function removePalette(PaletteInterface $palette); diff --git a/src/DataDefinition/Palette/Property.php b/src/DataDefinition/Palette/Property.php index 74ec45560..3b00cc9a2 100644 --- a/src/DataDefinition/Palette/Property.php +++ b/src/DataDefinition/Palette/Property.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,6 +24,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; /** @@ -40,16 +42,16 @@ class Property implements PropertyInterface /** * The condition to be examined to determine if this property is visible. * - * @var PropertyConditionInterface + * @var PropertyConditionInterface|null */ - protected $visibleCondition; + protected $visibleCondition = null; /** * The condition to be examined to determine if this property is editable. * - * @var PropertyConditionInterface + * @var PropertyConditionInterface|null */ - protected $editableCondition; + protected $editableCondition = null; /** * Create a new instance. @@ -66,7 +68,9 @@ public function __construct($name) */ public function setName($name) { - $this->name = (string) $name; + $this->name = $name; + + return $this; } /** @@ -82,10 +86,12 @@ public function getName() */ public function isVisible( ModelInterface $model = null, - PropertyValueBag $input = null, + PropertyValueBagInterface $input = null, LegendInterface $legend = null ) { if ($this->visibleCondition) { + // We should have defined the interfaces back in 2013... :/ + assert($input === null || $input instanceof PropertyValueBag); return $this->visibleCondition->match($model, $input, $this, $legend); } @@ -97,10 +103,12 @@ public function isVisible( */ public function isEditable( ModelInterface $model = null, - PropertyValueBag $input = null, + PropertyValueBagInterface $input = null, LegendInterface $legend = null ) { if ($this->editableCondition) { + // We should have defined the interfaces back in 2013... :/ + assert($input === null || $input instanceof PropertyValueBag); return $this->editableCondition->match($model, $input, $this, $legend); } diff --git a/src/DataDefinition/Palette/PropertyInterface.php b/src/DataDefinition/Palette/PropertyInterface.php index 82896db76..e88b3bd25 100644 --- a/src/DataDefinition/Palette/PropertyInterface.php +++ b/src/DataDefinition/Palette/PropertyInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; -use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; /** @@ -49,38 +50,40 @@ public function getName(); /** * Check the conditions, if this property is visible. * - * @param ModelInterface|null $model If given, sub palettes will be evaluated depending on the model. - * If no model is given, all properties will be returned, including sub palette - * properties. - * @param PropertyValueBag $input If given, sub palettes will be evaluated depending on the input data. - * If no model and no input data is given, all properties will be returned, - * including sub palette properties. - * @param LegendInterface $legend The legend the property is assigned to. + * @param ModelInterface|null $model If given, sub palettes will be evaluated depending on the model. + * If no model is given, all properties will be returned, including + * sub palette properties. + * @param PropertyValueBagInterface|null $input If given, sub palettes will be evaluated depending on the input + * data. + * If no model and no input data is given, all properties will be + * returned, including sub palette properties. + * @param LegendInterface|null $legend The legend the property is assigned to. * * @return bool */ public function isVisible( ModelInterface $model = null, - PropertyValueBag $input = null, + PropertyValueBagInterface $input = null, LegendInterface $legend = null ); /** * Check the conditions, if this property is editable. * - * @param ModelInterface|null $model If given, sub palettes will be evaluated depending on the model. - * If no model is given, all properties will be returned, including sub palette - * properties. - * @param PropertyValueBag $input If given, sub palettes will be evaluated depending on the input data. - * If no model and no input data is given, all properties will be returned, - * including sub palette properties. - * @param LegendInterface $legend The legend the property is assigned to. + * @param ModelInterface|null $model If given, sub palettes will be evaluated depending on the model. + * If no model is given, all properties will be returned, including + * sub palette properties. + * @param PropertyValueBagInterface|null $input If given, sub palettes will be evaluated depending on the input + * data. + * If no model and no input data is given, all properties will be + * returned, including sub palette properties. + * @param LegendInterface $legend The legend the property is assigned to. * * @return bool */ public function isEditable( ModelInterface $model = null, - PropertyValueBag $input = null, + PropertyValueBagInterface $input = null, LegendInterface $legend = null ); @@ -96,7 +99,7 @@ public function setVisibleCondition(PropertyConditionInterface $condition = null /** * Get the visible condition for this property. * - * @return PropertyConditionInterface + * @return PropertyConditionInterface|null */ public function getVisibleCondition(); @@ -112,7 +115,7 @@ public function setEditableCondition(PropertyConditionInterface $condition = nul /** * Get the editable condition for this property. * - * @return PropertyConditionInterface + * @return PropertyConditionInterface|null */ public function getEditableCondition(); diff --git a/src/DataDefinitionContainer.php b/src/DataDefinitionContainer.php index e2fb16762..b45c1bbaa 100644 --- a/src/DataDefinitionContainer.php +++ b/src/DataDefinitionContainer.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,16 +34,16 @@ class DataDefinitionContainer implements DataDefinitionContainerInterface /** * The definitions stored in the container. * - * @var ContainerInterface[] + * @var array */ - protected $definitions; + protected $definitions = []; /** * {@inheritDoc} */ public function setDefinition($name, $definition) { - if ($definition) { + if (null !== $definition) { $this->definitions[$name] = $definition; } else { unset($this->definitions[$name]); diff --git a/src/DataDefinitionContainerInterface.php b/src/DataDefinitionContainerInterface.php index 28861f7f1..bd8e98d7a 100644 --- a/src/DataDefinitionContainerInterface.php +++ b/src/DataDefinitionContainerInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,8 +33,8 @@ interface DataDefinitionContainerInterface /** * Add or override a definition in the container. * - * @param string $name Name of the definition. - * @param ContainerInterface $definition The definition to store. + * @param string $name Name of the definition. + * @param ContainerInterface|null $definition The definition to store - if null, the definition will get unset. * * @return DataDefinitionContainerInterface */ diff --git a/src/DefaultEnvironment.php b/src/DefaultEnvironment.php index b8e133aed..8dc394720 100644 --- a/src/DefaultEnvironment.php +++ b/src/DefaultEnvironment.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -271,7 +272,11 @@ public function getBaseConfigRegistry() public function hasDataProvider($source = null) { if (null === $source) { - $source = $this->getDataDefinition()->getBasicDefinition()->getDataProvider(); + $definition = $this->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $source = $definition->getBasicDefinition()->getDataProvider(); + assert(\is_string($source)); } return isset($this->arrDataProvider[$source]); @@ -282,28 +287,32 @@ public function hasDataProvider($source = null) * * @throws DcGeneralRuntimeException When an undefined provider is requested. */ - public function getDataProvider($source = null) + public function getDataProvider($strSource = null) { - if (null === $source) { - $source = $this->getDataDefinition()->getBasicDefinition()->getDataProvider(); + if (null === $strSource) { + $definition = $this->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $strSource = $definition->getBasicDefinition()->getDataProvider(); + assert(\is_string($strSource)); } - if (isset($this->arrDataProvider[$source])) { - return $this->arrDataProvider[$source]; + if (isset($this->arrDataProvider[$strSource])) { + return $this->arrDataProvider[$strSource]; } - throw new DcGeneralRuntimeException(\sprintf('Data provider %s not defined', $source)); + throw new DcGeneralRuntimeException(\sprintf('Data provider %s not defined', $strSource)); } /** * {@inheritdoc} */ - public function addDataProvider($source, $dataProvider) + public function addDataProvider($strSource, $dataProvider) { // Force removal of an potentially registered data provider to ease sub-classing. - $this->removeDataProvider($source); + $this->removeDataProvider($strSource); - $this->arrDataProvider[$source] = $dataProvider; + $this->arrDataProvider[$strSource] = $dataProvider; return $this; } @@ -311,10 +320,10 @@ public function addDataProvider($source, $dataProvider) /** * {@inheritdoc} */ - public function removeDataProvider($source) + public function removeDataProvider($strSource) { - if (isset($this->arrDataProvider[$source])) { - unset($this->arrDataProvider[$source]); + if (isset($this->arrDataProvider[$strSource])) { + unset($this->arrDataProvider[$strSource]); } return $this; @@ -331,13 +340,9 @@ public function getClipboard() /** * {@inheritdoc} */ - public function setClipboard($clipboard) + public function setClipboard($objClipboard) { - if (null === $clipboard) { - $this->objClipboard = null; - } else { - $this->objClipboard = $clipboard; - } + $this->objClipboard = $objClipboard; return $this; } @@ -345,13 +350,14 @@ public function setClipboard($clipboard) /** * {@inheritdoc} */ - public function setTranslator(TranslatorInterface $translator) + public function setTranslator(TranslatorInterface $manager) { - $this->translator = $translator; + $this->translator = $manager; return $this; } + /** * {@inheritdoc} */ diff --git a/src/DependencyInjection/CcaDcGeneralExtension.php b/src/DependencyInjection/CcaDcGeneralExtension.php index 21fc22bd7..3c7b1d298 100644 --- a/src/DependencyInjection/CcaDcGeneralExtension.php +++ b/src/DependencyInjection/CcaDcGeneralExtension.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Christian Schiffler - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -56,7 +57,7 @@ class CcaDcGeneralExtension extends Extension /** * {@inheritDoc} */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); foreach ($this->files as $file) { diff --git a/src/DependencyInjection/Compiler/AddSessionBagsPass.php b/src/DependencyInjection/Compiler/AddSessionBagsPass.php index 6e4a44e69..e4d9c1d53 100644 --- a/src/DependencyInjection/Compiler/AddSessionBagsPass.php +++ b/src/DependencyInjection/Compiler/AddSessionBagsPass.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,7 +32,7 @@ class AddSessionBagsPass implements CompilerPassInterface /** * {@inheritdoc} */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->has('session')) { return; diff --git a/src/EnvironmentInterface.php b/src/EnvironmentInterface.php index 32d0e9fdc..bab507938 100644 --- a/src/EnvironmentInterface.php +++ b/src/EnvironmentInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -41,11 +42,11 @@ interface EnvironmentInterface /** * Set the Controller for the current setup. * - * @param ControllerInterface $objController The controller to use. + * @param ControllerInterface $controller The controller to use. * * @return EnvironmentInterface */ - public function setController($objController); + public function setController($controller); /** * Retrieve the Controller from the current setup. @@ -57,11 +58,11 @@ public function getController(); /** * Set the View for the current setup. * - * @param ViewInterface $objView The view to use. + * @param ViewInterface $view The view to use. * * @return EnvironmentInterface */ - public function setView($objView); + public function setView($view); /** * Retrieve the View from the current setup. @@ -73,11 +74,11 @@ public function getView(); /** * Set the data definition for this instance. * - * @param ContainerInterface $objContainer The data definition container to store. + * @param ContainerInterface $dataDefinition The data definition container to store. * * @return EnvironmentInterface */ - public function setDataDefinition($objContainer); + public function setDataDefinition($dataDefinition); /** * Retrieve the data definition for this instance. @@ -89,11 +90,13 @@ public function getDataDefinition(); /** * Set the data definition of the parent container. * - * @param ContainerInterface $objContainer The data definition container to store. + * @param ContainerInterface $objParentDataDefinition The data definition container to store. * * @return EnvironmentInterface + * + * @SuppressWarnings(PHPMD.LongVariable) */ - public function setParentDataDefinition($objContainer); + public function setParentDataDefinition($objParentDataDefinition); /** * Retrieve the data definition for the parent container. This applies only when in parented mode. @@ -105,11 +108,11 @@ public function getParentDataDefinition(); /** * Set the data definition of the root container. * - * @param ContainerInterface $objContainer The data definition container to store. + * @param ContainerInterface $rootDataDefinition The data definition container to store. * * @return EnvironmentInterface */ - public function setRootDataDefinition($objContainer); + public function setRootDataDefinition($rootDataDefinition); /** * Retrieve the data definition for the root container. This applies only when in hierarchical mode. @@ -137,11 +140,11 @@ public function getSessionStorage(); /** * Set the input provider to use. * - * @param InputProviderInterface $objInputProvider The input provider to use. + * @param InputProviderInterface $inputProvider The input provider to use. * * @return EnvironmentInterface */ - public function setInputProvider($objInputProvider); + public function setInputProvider($inputProvider); /** * Retrieve the input provider. @@ -169,11 +172,11 @@ public function getBaseConfigRegistry(); /** * Determine if the data provider with the given name exists. * - * @param string|null $strSource The source name to check the providers for. + * @param string|null $source The source name to check the providers for. * * @return mixed */ - public function hasDataProvider($strSource = null); + public function hasDataProvider($source = null); /** * Retrieve the data provider for the named source. @@ -214,9 +217,9 @@ public function removeDataProvider($strSource); public function getClipboard(); /** - * Set the the clipboard. + * Set the clipboard. * - * @param ClipboardInterface $objClipboard Clipboard instance. + * @param ClipboardInterface|null $objClipboard Clipboard instance. * * @return EnvironmentInterface */ diff --git a/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php b/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php index cd1696180..2c513f91b 100644 --- a/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php +++ b/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -50,8 +51,9 @@ abstract class AbstractEventDrivenEnvironmentPopulator implements EnvironmentPop */ public static function process(PopulateEnvironmentEvent $event) { + /** @psalm-suppress UnsafeInstantiation */ $builder = new static(); - /** @var $builder EnvironmentPopulatorInterface */ + /** @var EnvironmentPopulatorInterface $builder */ $builder->populate($event->getEnvironment()); } } diff --git a/src/EventListener/ModelRelationship/ParentEnforcingListener.php b/src/EventListener/ModelRelationship/ParentEnforcingListener.php index 59e0d5df8..b35bc3f1f 100644 --- a/src/EventListener/ModelRelationship/ParentEnforcingListener.php +++ b/src/EventListener/ModelRelationship/ParentEnforcingListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,8 +24,11 @@ use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Controller\RelationshipManager; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Event\EnforceModelRelationshipEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; /** * This class takes care of enforcing a parent child relationship on a model. @@ -41,9 +45,14 @@ class ParentEnforcingListener public function process(EnforceModelRelationshipEvent $event) { $environment = $event->getEnvironment(); + $definition = $environment->getDataDefinition(); - $mode = $definition->getBasicDefinition()->getMode(); - $input = $environment->getInputProvider(); + assert($definition instanceof ContainerInterface); + + $mode = $definition->getBasicDefinition()->getMode(); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); if (BasicDefinitionInterface::MODE_PARENTEDLIST !== $mode || !$input->hasParameter('pid')) { return; @@ -52,6 +61,7 @@ public function process(EnforceModelRelationshipEvent $event) $model = $event->getModel(); $parent = (new ModelCollector($environment))->getModel(ModelId::fromSerialized($input->getParameter('pid'))); + assert($parent instanceof ModelInterface); (new RelationshipManager($definition->getModelRelationshipDefinition(), $mode))->setParent($model, $parent); } diff --git a/src/EventListener/ModelRelationship/TreeEnforcingListener.php b/src/EventListener/ModelRelationship/TreeEnforcingListener.php index 3fae9e259..12cccae24 100644 --- a/src/EventListener/ModelRelationship/TreeEnforcingListener.php +++ b/src/EventListener/ModelRelationship/TreeEnforcingListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,9 +25,12 @@ use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Controller\RelationshipManager; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Event\EnforceModelRelationshipEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; /** * This class takes care of enforcing a tree relationship on a model. @@ -43,17 +47,23 @@ class TreeEnforcingListener public function process(EnforceModelRelationshipEvent $event) { $environment = $event->getEnvironment(); - $mode = $environment->getDataDefinition()->getBasicDefinition()->getMode(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $mode = $definition->getBasicDefinition()->getMode(); if (BasicDefinitionInterface::MODE_HIERARCHICAL !== $mode) { return; } - $input = $environment->getInputProvider(); + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + $model = $event->getModel(); $collector = new ModelCollector($environment); $relationships = new RelationshipManager( - $environment->getDataDefinition()->getModelRelationshipDefinition(), + $definition->getModelRelationshipDefinition(), $mode ); @@ -76,6 +86,7 @@ public function process(EnforceModelRelationshipEvent $event) // Also enforce the parent condition of the parent provider (if any). if ($input->hasParameter('pid')) { $parent = $collector->getModel($input->getParameter('pid')); + assert($parent instanceof ModelInterface); $relationships->setParent($model, $parent); } } @@ -83,7 +94,7 @@ public function process(EnforceModelRelationshipEvent $event) /** * Handle paste into. * - * @param ModelId $into The id of the new parenting model. + * @param ModelIdInterface $into The id of the new parenting model. * @param RelationshipManager $relationships The relationship manager. * @param ModelCollector $collector The model collector. * @param ModelInterface $model The model. @@ -91,11 +102,11 @@ public function process(EnforceModelRelationshipEvent $event) * @return void */ private function handleInto( - ModelId $into, + ModelIdInterface $into, RelationshipManager $relationships, ModelCollector $collector, ModelInterface $model - ) { + ): void { // If we have a null, it means insert into the tree root. if (0 === $into->getId() || null === $parent = $collector->getModel($into)) { $relationships->setRoot($model); @@ -108,7 +119,7 @@ private function handleInto( /** * Handle paste after. * - * @param ModelId $after The id of the sibling model. + * @param ModelIdInterface $after The id of the sibling model. * @param RelationshipManager $relationships The relationship manager. * @param ModelCollector $collector The model collector. * @param ModelInterface $model The model. @@ -116,7 +127,7 @@ private function handleInto( * @return void */ private function handleAfter( - ModelId $after, + ModelIdInterface $after, RelationshipManager $relationships, ModelCollector $collector, ModelInterface $model @@ -129,6 +140,8 @@ private function handleAfter( } $parent = $collector->searchParentOf($sibling); + assert($parent instanceof ModelInterface); + $relationships->setParent($model, $parent); } } diff --git a/src/Factory/DcGeneralFactory.php b/src/Factory/DcGeneralFactory.php index 1fe0e6f79..519fd3dfb 100644 --- a/src/Factory/DcGeneralFactory.php +++ b/src/Factory/DcGeneralFactory.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Tristan Lins * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -49,9 +49,9 @@ class DcGeneralFactory implements DcGeneralFactoryInterface /** * The cache. * - * @var CacheInterface|null + * @var CacheInterface */ - private $cache; + private CacheInterface $cache; /** * The constructor. @@ -60,16 +60,19 @@ class DcGeneralFactory implements DcGeneralFactoryInterface */ public function __construct(CacheInterface $cache = null) { - $this->cache = $cache; - if (null === $this->cache) { + if (null === $cache) { // @codingStandardsIgnoreStart @\trigger_error( - 'You should use ' . DcGeneralFactoryCache::class . ' .', + 'You should pass an instance of ' . CacheInterface::class . ' .', E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - $this->cache = System::getContainer()->get(DcGeneralFactoryCache::class); + /** @psalm-suppress DeprecatedClass */ + $cache = System::getContainer()->get(DcGeneralFactoryCache::class); + + assert($cache instanceof CacheInterface); } + $this->cache = $cache; } /** @@ -84,11 +87,20 @@ public function __construct(CacheInterface $cache = null) */ public static function deriveEmptyFromEnvironment(EnvironmentInterface $environment) { + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $factory = new DcGeneralFactory(); - $factory->setEventDispatcher($environment->getEventDispatcher()); - $factory->setTranslator($environment->getTranslator()); + $factory->setEventDispatcher($dispatcher); + $factory->setTranslator($translator); $factory->setEnvironmentClassName(\get_class($environment)); - $factory->setContainerClassName(\get_class($environment->getDataDefinition())); + $factory->setContainerClassName(\get_class($definition)); return $factory; } @@ -104,14 +116,18 @@ public static function deriveEmptyFromEnvironment(EnvironmentInterface $environm public static function deriveFromEnvironment(EnvironmentInterface $environment) { $factory = static::deriveEmptyFromEnvironment($environment); - $factory->setContainerName($environment->getDataDefinition()->getName()); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $factory->setContainerName($definition->getName()); return $factory; } /** * The class name to use for the environment. * - * @var string + * @var class-string */ protected $environmentClassName = DefaultEnvironment::class; @@ -120,56 +136,56 @@ public static function deriveFromEnvironment(EnvironmentInterface $environment) * * @var string */ - protected $containerName; + protected $containerName = ''; /** * The class name of the class to use for the data definition container. * - * @var string + * @var class-string */ protected $containerClassName = DefaultContainer::class; /** * The class name of the class to use as DcGeneral. * - * @var string + * @var class-string */ protected $dcGeneralClassName = DcGeneral::class; /** * The event dispatcher to use. * - * @var EventDispatcherInterface + * @var EventDispatcherInterface|null */ - protected $eventDispatcher; + protected $eventDispatcher = null; /** * The translator that shall be used. * - * @var TranslatorInterface + * @var TranslatorInterface|null */ - protected $translator; + protected $translator = null; /** * The environment for the new instance. * - * @var EnvironmentInterface + * @var EnvironmentInterface|null */ - protected $environment; + protected $environment = null; /** * The data definition container instance. * - * @var ContainerInterface + * @var ContainerInterface|null */ - protected $dataContainer; + protected $dataContainer = null; /** * {@inheritdoc} */ public function setEnvironmentClassName($environmentClassName) { - $this->environmentClassName = (string) $environmentClassName; + $this->environmentClassName = $environmentClassName; return $this; } @@ -187,7 +203,7 @@ public function getEnvironmentClassName() */ public function setContainerName($containerName) { - $this->containerName = (string) $containerName; + $this->containerName = $containerName; return $this; } @@ -205,7 +221,7 @@ public function getContainerName() */ public function setContainerClassName($containerClassName) { - $this->containerClassName = (string) $containerClassName; + $this->containerClassName = $containerClassName; return $this; } @@ -223,7 +239,7 @@ public function getContainerClassName() */ public function setDcGeneralClassName($dcGeneralClassName) { - $this->dcGeneralClassName = (string) $dcGeneralClassName; + $this->dcGeneralClassName = $dcGeneralClassName; return $this; } @@ -251,6 +267,10 @@ public function setEventDispatcher($dispatcher) */ public function getEventDispatcher() { + if (null === $this->eventDispatcher) { + throw new DcGeneralRuntimeException('Required event dispatcher is missing'); + } + return $this->eventDispatcher; } @@ -269,6 +289,10 @@ public function setTranslator(TranslatorInterface $translator) */ public function getTranslator() { + if (null === $this->translator) { + throw new DcGeneralRuntimeException('Required translator is missing'); + } + return $this->translator; } @@ -324,13 +348,15 @@ public function createDcGeneral() } $cacheKey = \md5('dc-general.' . $this->containerName); - return $this->cache->get($cacheKey, function (): DcGeneral { + $cache = $this->cache; + assert($cache instanceof CacheInterface); + + return $cache->get($cacheKey, function (): DcGeneral { // Backwards compatibility. $this->getEventDispatcher()->dispatch(new PreCreateDcGeneralEvent($this), PreCreateDcGeneralEvent::NAME); $environment = $this->environment ?: $this->createEnvironment(); - /** @var DcGeneral $dcGeneral */ $dcGeneral = (new \ReflectionClass($this->dcGeneralClassName))->newInstance($environment); // Backwards compatibility. @@ -352,25 +378,16 @@ public function createEnvironment() throw new DcGeneralRuntimeException('Required container name or container is missing'); } - if (null === $this->eventDispatcher) { - throw new DcGeneralRuntimeException('Required event dispatcher is missing'); - } - - if (null === $this->translator) { - throw new DcGeneralRuntimeException('Required translator is missing'); - } - if ($this->dataContainer) { $dataContainer = clone $this->dataContainer; } else { $dataContainer = $this->createContainer(); } - /** @var EnvironmentInterface $environment */ $environment = (new \ReflectionClass($this->environmentClassName))->newInstance(); $environment->setDataDefinition($dataContainer); - $environment->setEventDispatcher($this->eventDispatcher); - $environment->setTranslator($this->translator); + $environment->setEventDispatcher($this->getEventDispatcher()); + $environment->setTranslator($this->getTranslator()); // Backwards compatibility. $this->getEventDispatcher()->dispatch( @@ -395,12 +412,9 @@ public function createContainer() throw new DcGeneralRuntimeException('Required container name is missing'); } - if (null === $this->eventDispatcher) { - throw new DcGeneralRuntimeException('Required event dispatcher is missing'); - } - /** @var DataDefinitionContainerInterface $definitions */ $definitions = System::getContainer()->get('cca.dc-general.data-definition-container'); + assert($definitions instanceof DataDefinitionContainerInterface); if ($definitions->hasDefinition($this->containerName)) { return clone $definitions->getDefinition($this->containerName); diff --git a/src/Factory/DcGeneralFactoryInterface.php b/src/Factory/DcGeneralFactoryInterface.php index 9ee9b3451..037d1e3b6 100644 --- a/src/Factory/DcGeneralFactoryInterface.php +++ b/src/Factory/DcGeneralFactoryInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,7 +38,7 @@ interface DcGeneralFactoryInterface /** * Set the class name to use as environment. * - * @param string $environmentClassName The class name. + * @param class-string $environmentClassName The class name. * * @return DcGeneralFactoryInterface */ @@ -46,7 +47,7 @@ public function setEnvironmentClassName($environmentClassName); /** * Retrieve the class name to use as environment. * - * @return string + * @return class-string */ public function getEnvironmentClassName(); @@ -69,7 +70,7 @@ public function getContainerName(); /** * Set the class name to use as data definition container. * - * @param string $containerClassName The class name. + * @param class-string $containerClassName The class name. * * @return DcGeneralFactoryInterface */ @@ -78,14 +79,14 @@ public function setContainerClassName($containerClassName); /** * Retrieve the class name to use as data definition container. * - * @return string + * @return class-string */ public function getContainerClassName(); /** * Set the class name to use as DcGeneral. * - * @param string $dcGeneralClassName The class name. + * @param class-string $dcGeneralClassName The class name. * * @return DcGeneralFactoryInterface */ @@ -94,7 +95,7 @@ public function setDcGeneralClassName($dcGeneralClassName); /** * Retrieve the class name to use as DcGeneral. * - * @return string + * @return class-string */ public function getDcGeneralClassName(); @@ -133,32 +134,32 @@ public function getTranslator(); /** * Set the environment to use. * - * @param EnvironmentInterface $environment The environment instance. + * @param EnvironmentInterface|null $environment The environment instance. * * @return DcGeneralFactoryInterface */ - public function setEnvironment(EnvironmentInterface $environment = null); + public function setEnvironment(?EnvironmentInterface $environment = null); /** * Retrieve the environment to use. * - * @return EnvironmentInterface + * @return EnvironmentInterface|null */ public function getEnvironment(); /** * Set the data definition container to use. * - * @param ContainerInterface $dataContainer The data definition container instance. + * @param ContainerInterface|null $dataContainer The data definition container instance. * * @return DcGeneralFactoryInterface */ - public function setDataContainer(ContainerInterface $dataContainer = null); + public function setDataContainer(?ContainerInterface $dataContainer = null); /** * Retrieve the data definition container. * - * @return ContainerInterface + * @return ContainerInterface|null */ public function getDataContainer(); diff --git a/src/Panel/AbstractElement.php b/src/Panel/AbstractElement.php index 41ac490fe..10ffd98a3 100644 --- a/src/Panel/AbstractElement.php +++ b/src/Panel/AbstractElement.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,13 +14,15 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Panel; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; @@ -34,18 +36,18 @@ abstract class AbstractElement implements PanelElementInterface /** * The panel this element is contained within. * - * @var PanelInterface + * @var PanelInterface|null */ - protected $objPanel; + protected $objPanel = null; /** * The base configuration that contains all filter, sorting and limit information for all other panel elements. * * This is used for determining the valid values in filters etc. * - * @var ConfigInterface + * @var ConfigInterface|null */ - private $objOtherConfig; + private $objOtherConfig = null; /** * Convenience method to retrieve Environment for this element. @@ -64,7 +66,10 @@ public function getEnvironment() */ public function getSessionStorage() { - return $this->getEnvironment()->getSessionStorage(); + $sessionStorage = $this->getEnvironment()->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + return $sessionStorage; } /** @@ -74,7 +79,10 @@ public function getSessionStorage() */ public function getInputProvider() { - return $this->getEnvironment()->getInputProvider(); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return $inputProvider; } /** @@ -82,15 +90,19 @@ public function getInputProvider() */ public function getPanel() { + if (null === $this->objPanel) { + throw new \LogicException('Panel not set'); + } + return $this->objPanel; } /** * {@inheritDoc} */ - public function setPanel(PanelInterface $panel) + public function setPanel(PanelInterface $panelElement) { - $this->objPanel = $panel; + $this->objPanel = $panelElement; return $this; } @@ -102,11 +114,11 @@ public function setPanel(PanelInterface $panel) */ protected function getOtherConfig() { - if (!isset($this->objOtherConfig)) { - $this->objOtherConfig = $this - ->getEnvironment() - ->getBaseConfigRegistry() - ->getBaseConfig(); + if (null === $this->objOtherConfig) { + $registry = $this->getEnvironment()->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $this->objOtherConfig = $registry->getBaseConfig(); $this ->getPanel() diff --git a/src/Panel/DefaultFilterElement.php b/src/Panel/DefaultFilterElement.php index 9409a03d2..2509cc89b 100644 --- a/src/Panel/DefaultFilterElement.php +++ b/src/Panel/DefaultFilterElement.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author Cliff Parnitzky - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,7 +25,11 @@ namespace ContaoCommunityAlliance\DcGeneral\Panel; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; /** @@ -38,7 +42,7 @@ class DefaultFilterElement extends AbstractElement implements FilterElementInter * * @var string */ - private $strProperty; + private $strProperty = 'string'; /** * The current value of this filter. @@ -52,7 +56,7 @@ class DefaultFilterElement extends AbstractElement implements FilterElementInter * * @var array */ - private $arrFilterOptions; + private $arrFilterOptions = []; /** * Retrieve the persistent value from the input provider. @@ -66,8 +70,11 @@ protected function getPersistent() $values = $this->getSessionStorage()->get('filter'); } - if (\array_key_exists($this->getEnvironment()->getDataDefinition()->getName(), $values)) { - $values = $values[$this->getEnvironment()->getDataDefinition()->getName()]; + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (\array_key_exists($definition->getName(), $values)) { + $values = $values[$definition->getName()]; if (\array_key_exists($this->getPropertyName(), $values)) { return $values[$this->getPropertyName()]; @@ -86,7 +93,10 @@ protected function getPersistent() */ protected function setPersistent($value) { - $definitionName = $this->getEnvironment()->getDataDefinition()->getName(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $definitionName = $definition->getName(); $values = []; @@ -95,7 +105,7 @@ protected function setPersistent($value) } if (isset($values[$definitionName]) && !\is_array($values[$definitionName])) { - $values[$this->getEnvironment()->getDataDefinition()->getName()] = []; + $values[$definition->getName()] = []; } if ((null !== $values) && ($value !== 'tl_' . $this->getPropertyName())) { @@ -115,10 +125,14 @@ protected function setPersistent($value) private function updateValue() { $session = $this->getSessionStorage(); - $input = $this->getInputProvider(); + assert($session instanceof SessionStorageInterface); + + $input = $this->getInputProvider(); + assert($input instanceof InputProviderInterface); + $value = null; - if ('1' !== $this->getEnvironment()->getInputProvider()->getValue('filter_reset')) { + if ('1' !== $input->getValue('filter_reset')) { if ($input->hasValue($this->getPropertyName()) && $this->getPanel()->getContainer()->updateValues()) { $value = $input->getValue($this->getPropertyName()); @@ -148,14 +162,14 @@ private function loadFilterOptions() $otherConfig = $this->getOtherConfig(); $otherConfig->setFields([$this->getPropertyName()]); - $filterOptions = $this - ->getEnvironment() - ->getDataProvider() - ->getFilterOptions($otherConfig); + $dataProvider = $this->getEnvironment()->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + + $filterOptions = $dataProvider->getFilterOptions($otherConfig); $options = []; foreach ($filterOptions as $filterKey => $filterValue) { - $options[(string) $filterKey] = $filterValue; + $options[$filterKey] = $filterValue; } $this->arrFilterOptions = $options; @@ -193,10 +207,10 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme */ public function render(ViewTemplateInterface $viewTemplate) { - $labels = $this - ->getEnvironment() - ->getDataDefinition() - ->getPropertiesDefinition() + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $labels = $definition->getPropertiesDefinition() ->getProperty($this->getPropertyName())->getLabel(); $options = [ @@ -216,7 +230,7 @@ public function render(ViewTemplateInterface $viewTemplate) ]; } - $viewTemplate->set('label', (\is_array($labels) ? $labels[0] : $labels)); + $viewTemplate->set('label', $labels); $viewTemplate->set('name', $this->getPropertyName()); $viewTemplate->set('id', $this->getPropertyName()); $viewTemplate->set('class', 'tl_select' . ((null !== $selectedValue) ? ' active' : '')); diff --git a/src/Panel/DefaultLimitElement.php b/src/Panel/DefaultLimitElement.php index fb513b21c..8d939278a 100644 --- a/src/Panel/DefaultLimitElement.php +++ b/src/Panel/DefaultLimitElement.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author Cliff Parnitzky - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +26,9 @@ use Contao\Config; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; /** @@ -38,21 +41,21 @@ class DefaultLimitElement extends AbstractElement implements LimitElementInterfa * * @var int */ - private $intOffset; + private $intOffset = 0; /** * The current amount. * * @var int */ - private $intAmount; + private $intAmount = 0; /** * The total amount of all valid entries. * * @var int */ - private $intTotal; + private $intTotal = 0; /** * Retrieve the amount of items to display per page. @@ -82,24 +85,19 @@ protected function getMaxItemsPerPage() protected function calculateTotal() { $otherConfig = $this->getOtherConfig(); - $total = $this - ->getEnvironment() - ->getDataProvider() - ->fetchAll($otherConfig->setIdOnly(true)); - if (\is_array($total)) { - $this->intTotal = $total ? \count($total) : 0; + $dataProvider = $this->getEnvironment()->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); - return; - } + $total = $dataProvider->fetchAll($otherConfig->setIdOnly(true)); - if (\is_object($total)) { - $this->intTotal = $total->length(); + if (\is_array($total)) { + $this->intTotal = $total ? \count($total) : 0; return; } - $this->intTotal = 0; + $this->intTotal = $total->length(); } /** @@ -114,8 +112,11 @@ protected function getPersistent() $values = $this->getSessionStorage()->get('limit'); } - if (\array_key_exists($this->getEnvironment()->getDataDefinition()->getName(), $values)) { - return $values[$this->getEnvironment()->getDataDefinition()->getName()]; + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (\array_key_exists($definition->getName(), $values)) { + return $values[$definition->getName()]; } return []; @@ -131,7 +132,10 @@ protected function getPersistent() */ protected function setPersistent($offset, $amount) { - $definitionName = $this->getEnvironment()->getDataDefinition()->getName(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $definitionName = $definition->getName(); $values = []; @@ -172,11 +176,6 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme $this->defineOffsetAndAmountOption($offset, $amount); - if (null !== $offset) { - $this->setOffset($offset); - $this->setAmount($amount); - } - $config->setStart($this->getOffset()); $config->setAmount($this->getAmount()); } @@ -191,8 +190,11 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme */ private function defineOffsetAndAmountOption(int &$offset, int &$amount): void { - if ('1' === $this->getEnvironment()->getInputProvider()->getValue('filter_reset')) { - $this->setPersistent(null, null); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if ('1' === $inputProvider->getValue('filter_reset')) { + $this->setPersistent(0, 0); return; } @@ -200,8 +202,8 @@ private function defineOffsetAndAmountOption(int &$offset, int &$amount): void if ($input->hasValue('tl_limit') && $this->getPanel()->getContainer()->updateValues()) { $limit = $input->getValue('tl_limit'); if ('tl_limit' !== $limit) { - [$offset, $amount] = \explode(',', $input->getValue('tl_limit')) + [null, null]; - $this->setPersistent($offset, $amount); + [$offset, $amount] = \explode(',', $input->getValue('tl_limit')) + [0, 0]; + $this->setPersistent((int) $offset, (int) $amount); } } @@ -295,9 +297,9 @@ public function render(ViewTemplateInterface $viewTemplate) /** * {@inheritDoc} */ - public function setOffset($offset) + public function setOffset($intOffset) { - $this->intOffset = (int) $offset; + $this->intOffset = $intOffset; return $this; } diff --git a/src/Panel/DefaultPanel.php b/src/Panel/DefaultPanel.php index 845069858..2db61511d 100644 --- a/src/Panel/DefaultPanel.php +++ b/src/Panel/DefaultPanel.php @@ -32,16 +32,16 @@ class DefaultPanel implements PanelInterface /** * The panel container this panel is contained within. * - * @var PanelContainerInterface + * @var PanelContainerInterface|null */ - private $objContainer; + private ?PanelContainerInterface $objContainer = null; /** * The elements contained within this panel. * - * @var PanelElementInterface[] + * @var array */ - private $arrElements; + private array $arrElements; /** * Create a new instance. @@ -56,6 +56,10 @@ public function __construct() */ public function getContainer() { + if (null === $this->objContainer) { + throw new \LogicException('Container for panel is not set.'); + } + return $this->objContainer; } diff --git a/src/Panel/DefaultPanelContainer.php b/src/Panel/DefaultPanelContainer.php index 71ae322a1..0643f8468 100644 --- a/src/Panel/DefaultPanelContainer.php +++ b/src/Panel/DefaultPanelContainer.php @@ -24,6 +24,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; /** * Default implementation of a panel container. @@ -33,31 +34,36 @@ class DefaultPanelContainer implements PanelContainerInterface /** * The environment in use. * - * @var EnvironmentInterface + * @var EnvironmentInterface|null */ - private $objEnvironment; + private $objEnvironment = null; /** * The panels contained within this container. * - * @var PanelInterface[] + * @var array */ - private $arrPanels = []; + private array $panels = []; /** * {@inheritdoc} */ public function getEnvironment() { + if (null === $this->objEnvironment) { + throw new \LogicException('Environment for panel is not set.'); + } + return $this->objEnvironment; } /** * {@inheritdoc} */ - public function setEnvironment(EnvironmentInterface $environment) + public function setEnvironment(EnvironmentInterface $objEnvironment) { - $this->objEnvironment = $environment; + $this->objEnvironment = $objEnvironment; + return $this; } @@ -66,7 +72,7 @@ public function setEnvironment(EnvironmentInterface $environment) */ public function addPanel($panelName, $panel) { - $this->arrPanels[$panelName] = $panel; + $this->panels[$panelName] = $panel; $panel->setContainer($this); return $this; @@ -77,7 +83,7 @@ public function addPanel($panelName, $panel) */ public function getPanel($panelName) { - return $this->arrPanels[$panelName]; + return $this->panels[$panelName]; } /** @@ -89,6 +95,8 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme foreach ($this as $panel) { $panel->initialize($config, $element); } + + return $this; } /** @@ -96,7 +104,10 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme */ public function updateValues() { - return ('tl_filters' === $this->getEnvironment()->getInputProvider()->getValue('FORM_SUBMIT')); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return ('tl_filters' === $inputProvider->getValue('FORM_SUBMIT')); } /** @@ -104,7 +115,7 @@ public function updateValues() */ public function getIterator(): \Traversable { - return new \ArrayIterator($this->arrPanels); + return new \ArrayIterator($this->panels); } /** @@ -112,6 +123,6 @@ public function getIterator(): \Traversable */ public function count(): int { - return \count($this->arrPanels); + return \count($this->panels); } } diff --git a/src/Panel/DefaultSearchElement.php b/src/Panel/DefaultSearchElement.php index 5f11d57a2..72f3d2dd4 100644 --- a/src/Panel/DefaultSearchElement.php +++ b/src/Panel/DefaultSearchElement.php @@ -24,6 +24,8 @@ namespace ContaoCommunityAlliance\DcGeneral\Panel; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; /** @@ -36,21 +38,21 @@ class DefaultSearchElement extends AbstractElement implements SearchElementInter * * @var array */ - private $arrProperties; + private array $arrProperties = []; /** * The currently active property to be searched on. * * @var string */ - private $strSelectedProperty; + private string $strSelectedProperty = ''; /** * The current value to be searched. * * @var mixed */ - private $mixValue; + private mixed $mixValue = null; /** * Retrieve the persistent value from the input provider. @@ -59,13 +61,16 @@ class DefaultSearchElement extends AbstractElement implements SearchElementInter */ protected function getPersistent() { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $values = []; if ($this->getSessionStorage()->has('search')) { $values = $this->getSessionStorage()->get('search'); } - if (\array_key_exists($this->getEnvironment()->getDataDefinition()->getName(), $values)) { - return $values[$this->getEnvironment()->getDataDefinition()->getName()]; + if (\array_key_exists($definition->getName(), $values)) { + return $values[$definition->getName()]; } return []; @@ -81,9 +86,12 @@ protected function getPersistent() */ protected function setPersistent($propertyName, $searchValue) { - $values = []; - $definitionName = $this->getEnvironment()->getDataDefinition()->getName(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $definitionName = $definition->getName(); + $values = []; if ($this->getSessionStorage()->has('search')) { $values = $this->getSessionStorage()->get('search'); } @@ -93,12 +101,8 @@ protected function setPersistent($propertyName, $searchValue) $values[$definitionName] = []; } - if ($searchValue) { - $values[$definitionName]['field'] = $propertyName; - $values[$definitionName]['value'] = $searchValue; - } else { - unset($values[$definitionName]); - } + $values[$definitionName]['field'] = $propertyName; + $values[$definitionName]['value'] = $searchValue; } else { unset($values[$definitionName]); } @@ -109,14 +113,16 @@ protected function setPersistent($propertyName, $searchValue) /** * {@inheritdoc} */ - public function initialize(ConfigInterface $filterConfig, PanelElementInterface $objElement = null) + public function initialize(ConfigInterface $config, PanelElementInterface $element = null) { $session = $this->getSessionStorage(); $input = $this->getInputProvider(); - $value = null; - $field = null; + assert($input instanceof InputProviderInterface); + + $value = ''; + $field = ''; - if ('1' !== $this->getEnvironment()->getInputProvider()->getValue('filter_reset')) { + if ('1' !== $input->getValue('filter_reset')) { if ($input->hasValue('tl_field') && $this->getPanel()->getContainer()->updateValues()) { $field = $input->getValue('tl_field'); $value = $input->getValue('tl_value'); @@ -133,19 +139,19 @@ public function initialize(ConfigInterface $filterConfig, PanelElementInterface $this->setSelectedProperty($field); $this->setValue($value); } else { - $this->setPersistent(null, null); + $this->setPersistent('', ''); } if (!($this->getSelectedProperty() && $this->getValue())) { return; } - $currents = $filterConfig->getFilter(); + $currents = $config->getFilter(); if (!\is_array($currents)) { $currents = []; } - $filterConfig->setFilter( + $config->setFilter( \array_merge_recursive( $currents, [ @@ -164,20 +170,21 @@ public function initialize(ConfigInterface $filterConfig, PanelElementInterface */ public function render(ViewTemplateInterface $viewTemplate) { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $options = []; foreach ($this->getPropertyNames() as $field) { - $lLabels = $this - ->getEnvironment() - ->getDataDefinition() + $lLabels = $definition ->getPropertiesDefinition() ->getProperty($field) ->getLabel(); $options[] = [ - 'value' => $field, - 'content' => \is_array($lLabels) ? $lLabels[0] : $lLabels, - 'attributes' => ($field === $this->getSelectedProperty()) ? ' selected' : '' - ]; + 'value' => $field, + 'content' => $lLabels, + 'attributes' => ($field === $this->getSelectedProperty()) ? ' selected' : '' + ]; } $viewTemplate->set('class', 'tl_select' . (!empty($this->getValue()) ? ' active' : '')); @@ -190,9 +197,9 @@ public function render(ViewTemplateInterface $viewTemplate) /** * {@inheritDoc} */ - public function addProperty($propertyName) + public function addProperty($strProperty) { - $this->arrProperties[] = $propertyName; + $this->arrProperties[] = $strProperty; return $this; } @@ -208,9 +215,9 @@ public function getPropertyNames(): array /** * {@inheritDoc} */ - public function setSelectedProperty($propertyName = '') + public function setSelectedProperty($strProperty = '') { - $this->strSelectedProperty = $propertyName; + $this->strSelectedProperty = $strProperty; return $this; } diff --git a/src/Panel/DefaultSortElement.php b/src/Panel/DefaultSortElement.php index 22346d940..3f5b83a5f 100644 --- a/src/Panel/DefaultSortElement.php +++ b/src/Panel/DefaultSortElement.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author Cliff Parnitzky - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,10 +27,11 @@ use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionCollectionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; -use phpDocumentor\Reflection\Types\Mixed_; /** * Default implementation of a sort element. @@ -40,21 +41,22 @@ class DefaultSortElement extends AbstractElement implements SortElementInterface /** * The selected definition. * - * @var GroupAndSortingDefinitionInterface + * @var GroupAndSortingDefinitionInterface|null */ - private $selected; + private $selected = null; /** * Retrieve the group and sorting definition. * - * @return GroupAndSortingDefinitionCollectionInterface|GroupAndSortingDefinitionInterface[] + * @return GroupAndSortingDefinitionCollectionInterface */ protected function getGroupAndSortingDefinition() { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $view */ - $view = $this->getEnvironment() - ->getDataDefinition() - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $view = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); return $view ->getListingConfig() @@ -62,7 +64,7 @@ protected function getGroupAndSortingDefinition() } /** - * Search a definition by it's name. + * Search a definition by its name. * * @param string $name The name. * @@ -82,20 +84,23 @@ protected function searchDefinitionByName($name) /** * Retrieve the persistent value from the input provider. * - * @return array + * @return string */ protected function getPersistent() { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $values = []; if ($this->getSessionStorage()->has('sorting')) { $values = $this->getSessionStorage()->get('sorting'); } - if (\array_key_exists($this->getEnvironment()->getDataDefinition()->getName(), $values)) { - return $values[$this->getEnvironment()->getDataDefinition()->getName()]; + if (\array_key_exists($definition->getName(), $values)) { + return $values[$definition->getName()]; } - return []; + return ''; } /** @@ -107,18 +112,18 @@ protected function getPersistent() */ protected function setPersistent($propertyName) { - $values = []; - $definitionName = $this->getEnvironment()->getDataDefinition()->getName(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $definitionName = $definition->getName(); + + $values = []; if ($this->getSessionStorage()->has('sorting')) { $values = $this->getSessionStorage()->get('sorting'); } if ($propertyName) { - if (isset($values[$definitionName]) && !\is_array($values[$definitionName])) { - $values[$definitionName] = []; - } - $values[$definitionName] = $propertyName; } else { unset($values[$definitionName]); @@ -130,18 +135,14 @@ protected function setPersistent($propertyName) /** * {@inheritDoc} */ - public function initialize(ConfigInterface $config, PanelElementInterface $panelElement = null) + public function initialize(ConfigInterface $config, PanelElementInterface $element = null) { - $this->defineSortOption($panelElement); + $this->defineSortOption($element); $current = $config->getSorting(); - if (!\is_array($current)) { - $current = []; - } - - if ($this->getSelectedDefinition()) { - foreach ($this->getSelectedDefinition() as $information) { + if (null !== $selected = $this->getSelectedDefinition()) { + foreach ($selected as $information) { $current[$information->getProperty()] = $information->getSortingMode(); } } @@ -162,9 +163,11 @@ private function defineSortOption(?PanelElementInterface $panelElement): void } $input = $this->getInputProvider(); - $value = null; + assert($input instanceof InputProviderInterface); - if ('1' !== $this->getEnvironment()->getInputProvider()->getValue('filter_reset')) { + $value = ''; + + if ('1' !== $input->getValue('filter_reset')) { if ($input->hasValue('tl_sort') && $this->getPanel()->getContainer()->updateValues()) { $value = $input->getValue('tl_sort'); @@ -181,7 +184,7 @@ private function defineSortOption(?PanelElementInterface $panelElement): void $this->setSelected($persistent); } else { - $this->setPersistent(null); + $this->setPersistent(''); } } @@ -190,11 +193,14 @@ private function defineSortOption(?PanelElementInterface $panelElement): void */ public function render(ViewTemplateInterface $viewTemplate) { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $options = []; foreach ($this->getGroupAndSortingDefinition() as $information) { /** @var GroupAndSortingDefinitionInterface $information */ $name = $information->getName(); - $properties = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + $properties = $definition->getPropertiesDefinition(); if ($properties->hasProperty($name)) { $name = $properties->getProperty($name)->getLabel(); } diff --git a/src/Panel/DefaultSubmitElement.php b/src/Panel/DefaultSubmitElement.php index 7e6ffd5b1..fdd169906 100644 --- a/src/Panel/DefaultSubmitElement.php +++ b/src/Panel/DefaultSubmitElement.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,7 +33,7 @@ class DefaultSubmitElement extends AbstractElement implements SubmitElementInter /** * {@inheritdoc} */ - public function initialize(ConfigInterface $config, PanelElementInterface $panelElement = null) + public function initialize(ConfigInterface $config, PanelElementInterface $element = null) { } @@ -41,5 +42,6 @@ public function initialize(ConfigInterface $config, PanelElementInterface $panel */ public function render(ViewTemplateInterface $viewTemplate) { + return $this; } } diff --git a/src/Panel/PanelContainerInterface.php b/src/Panel/PanelContainerInterface.php index d59602a59..b38309fda 100644 --- a/src/Panel/PanelContainerInterface.php +++ b/src/Panel/PanelContainerInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,6 +29,8 @@ * This interface describes a panel container. * * A Panel container contains panels which contain panel elements. + * + * @extends \IteratorAggregate */ interface PanelContainerInterface extends \IteratorAggregate, \Countable { @@ -50,31 +53,31 @@ public function setEnvironment(EnvironmentInterface $objEnvironment); /** * Add a panel to the container. * - * @param string $strKey Name of the panel. - * @param PanelInterface $objPanel The panel to add. + * @param string $panelName Name of the panel. + * @param PanelInterface $panel The panel to add. * * @return PanelContainerInterface */ - public function addPanel($strKey, $objPanel); + public function addPanel($panelName, $panel); /** * Retrieve a panel from the container. * - * @param string $strKey The name of the panel. + * @param string $panelName The name of the panel. * * @return PanelInterface */ - public function getPanel($strKey); + public function getPanel($panelName); /** * Initialize all panels and apply all restrictions to the given Config. * - * @param ConfigInterface $objConfig The data config to be populated with the element values. - * @param PanelElementInterface $objElement The element currently being initialized. + * @param ConfigInterface $config The data config to be populated with the element values. + * @param PanelElementInterface|null $element The element currently being initialized. * * @return PanelContainerInterface */ - public function initialize(ConfigInterface $objConfig, PanelElementInterface $objElement = null); + public function initialize(ConfigInterface $config, PanelElementInterface $element = null); /** * Determinator if the panels should be updated from the InputProvider or not. diff --git a/src/Panel/PanelElementInterface.php b/src/Panel/PanelElementInterface.php index 3cef7230a..825d28bd6 100644 --- a/src/Panel/PanelElementInterface.php +++ b/src/Panel/PanelElementInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -39,28 +40,28 @@ public function getPanel(); /** * Return the parenting panel. * - * @param PanelInterface $objPanel The panel to use as parent. + * @param PanelInterface $panelElement The panel to use as parent. * * @return PanelElementInterface */ - public function setPanel(PanelInterface $objPanel); + public function setPanel(PanelInterface $panelElement); /** * Initialize the passed configuration with the values of the element. * - * @param ConfigInterface $objConfig The config to which the initialization shall be applied to. - * @param PanelElementInterface $objElement The element to be initialized (if any). + * @param ConfigInterface $config The config to which the initialization shall be applied to. + * @param PanelElementInterface|null $element The element to be initialized (if any). * * @return void */ - public function initialize(ConfigInterface $objConfig, PanelElementInterface $objElement = null); + public function initialize(ConfigInterface $config, PanelElementInterface $element = null); /** * Render the element using the given Template. * - * @param ViewTemplateInterface $objTemplate The Template to use. + * @param ViewTemplateInterface $viewTemplate The Template to use. * - * @return PanelElementInterface + * @return self */ - public function render(ViewTemplateInterface $objTemplate); + public function render(ViewTemplateInterface $viewTemplate); } diff --git a/src/Panel/PanelInterface.php b/src/Panel/PanelInterface.php index 4d8531057..ee8259284 100644 --- a/src/Panel/PanelInterface.php +++ b/src/Panel/PanelInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,6 +28,8 @@ * This interface describes a panel. * * A panel is a row of a panel container. + * + * @extends \IteratorAggregate */ interface PanelInterface extends \IteratorAggregate, \Countable { @@ -40,38 +43,38 @@ public function getContainer(); /** * Set the parenting container. * - * @param PanelContainerInterface $objContainer The Container to be used as parent. + * @param PanelContainerInterface $container The Container to be used as parent. * * @return PanelInterface */ - public function setContainer(PanelContainerInterface $objContainer); + public function setContainer(PanelContainerInterface $container); /** * Add an element to the panel. * - * @param string $strKey Name of the panel. - * @param PanelElementInterface $objElement The element instance to add. + * @param string $panelName Name of the panel. + * @param PanelElementInterface $element The element instance to add. * * @return mixed */ - public function addElement($strKey, $objElement); + public function addElement($panelName, $element); /** * Retrieve an element with the given name. * - * @param string $strKey The name of the element. + * @param string $elementName The name of the element. * - * @return PanelElementInterface + * @return PanelElementInterface|null */ - public function getElement($strKey); + public function getElement($elementName); /** * Initialize the passed config via all contained elements. * - * @param ConfigInterface $objConfig The config to which the initialization shall be applied to. - * @param PanelElementInterface $objElement The element to be initialized (if any). + * @param ConfigInterface $config The config to which the initialization shall be applied to. + * @param PanelElementInterface|null $element The element to be initialized (if any). * * @return void */ - public function initialize(ConfigInterface $objConfig, PanelElementInterface $objElement = null); + public function initialize(ConfigInterface $config, PanelElementInterface $element = null); } diff --git a/src/Panel/SortElementInterface.php b/src/Panel/SortElementInterface.php index bf052a929..21cbe3834 100644 --- a/src/Panel/SortElementInterface.php +++ b/src/Panel/SortElementInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,6 @@ namespace ContaoCommunityAlliance\DcGeneral\Panel; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; /** * This interface describes a sort panel element. @@ -34,21 +34,21 @@ interface SortElementInterface extends PanelElementInterface * * @param string $name The name of the definition to mark as selected. * - * @return SearchElementInterface + * @return self */ public function setSelected($name); /** * Return the name of the currently selected definition. * - * @return string + * @return string|null */ public function getSelected(); /** * Return the currently selected definition. * - * @return GroupAndSortingDefinitionInterface|GroupAndSortingInformationInterface[] + * @return GroupAndSortingDefinitionInterface|null */ public function getSelectedDefinition(); } diff --git a/src/Resources/config/contao/picker_provider.yml b/src/Resources/config/contao/picker_provider.yml index 5a7765fe3..03964f72c 100644 --- a/src/Resources/config/contao/picker_provider.yml +++ b/src/Resources/config/contao/picker_provider.yml @@ -5,10 +5,12 @@ services: arguments: - "@knp_menu.factory" - "@router" + - "@?translator" + - "@security.helper" calls: - [setTokenStorage, ["@security.token_storage"]] tags: - - name: contao.picker_provider + - name: contao.picker_provider cca.picker.tree_picker_provider: class: ContaoCommunityAlliance\DcGeneral\Contao\Picker\TreePickerProvider @@ -20,4 +22,4 @@ services: calls: - [setTokenStorage, ["@security.token_storage"]] tags: - - name: contao.picker_provider + - name: contao.picker_provider diff --git a/src/Resources/contao/templates/dcbe_general_edit.html5 b/src/Resources/contao/templates/dcbe_general_edit.html5 index 12f120c47..5665d9403 100644 --- a/src/Resources/contao/templates/dcbe_general_edit.html5 +++ b/src/Resources/contao/templates/dcbe_general_edit.html5 @@ -74,7 +74,7 @@ $this->insert( -fieldsets as $arrFieldset): if($arrFieldset['legend']): ?> +fieldsets as $arrFieldset): if($arrFieldset['legend'] ?? null): ?>
diff --git a/src/View/ActionHandler/AbstractEnvironmentAwareHandler.php b/src/View/ActionHandler/AbstractEnvironmentAwareHandler.php index 8ac25cf8f..c33b98013 100644 --- a/src/View/ActionHandler/AbstractEnvironmentAwareHandler.php +++ b/src/View/ActionHandler/AbstractEnvironmentAwareHandler.php @@ -26,11 +26,13 @@ use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; /** - * The AbstractEnvironmentAwareHandler is designed for action handlers which can also be used in a non event context. + * The AbstractEnvironmentAwareHandler is designed for action handlers which can also be used in a nonevent context. * * It provides a setEnvironment method which has to be used to initialize the environment instead. * * @deprecated This class is deprecated as it is an event listener with a changing state and will get removed. + * + * @psalm-suppress DeprecatedClass */ abstract class AbstractEnvironmentAwareHandler extends AbstractHandler implements EnvironmentAwareInterface { diff --git a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php index 7b2a60514..5f2a59786 100644 --- a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php +++ b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php @@ -14,7 +14,7 @@ * @author Sven Baumann * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2013-2023s Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -31,6 +31,8 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\EditInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; @@ -43,7 +45,9 @@ use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; use LogicException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class is the abstract base for override/edit all "overrideAll/editAll" commands. @@ -53,6 +57,8 @@ */ abstract class AbstractPropertyOverrideEditAllHandler extends AbstractPropertyVisibilityHandler { + use CallActionTrait; + /** * Handle submit triggered button. * @@ -69,6 +75,7 @@ protected function handleSubmit(Action $action, EnvironmentInterface $environmen $inputProvider = $this->getInputProvider($environment); $sessionStorage = $this->getSessionStorage($environment); $eventDispatcher = $environment->getEventDispatcher(); + assert($eventDispatcher instanceof EventDispatcherInterface); if ( ('auto' === $inputProvider->getValue('SUBMIT_TYPE')) @@ -110,6 +117,7 @@ protected function editCollection( ) { while ($collection->count() > 0) { $model = $collection->shift(); + assert($model instanceof ModelInterface); $persistPropertyValueBag = $this->cloneCleanPropertyValueBag($action, $propertyValueBag, $model, $environment); @@ -131,6 +139,7 @@ protected function editCollection( protected function updateErrorInformation( \ArrayObject $renderInformation ) { + /** @var array>>|null $modelError */ $modelError = $renderInformation->offsetExists('modelError') ? $renderInformation->offsetGet('modelError') : null; @@ -232,8 +241,10 @@ private function markPropertyInvalidErrorsByModel( EnvironmentInterface $environment ) { $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); + $errorInformation = $editInformation->getModelError($model); - if (null === $errorInformation) { + if (!$errorInformation) { return; } @@ -301,7 +312,9 @@ private function updatePropertyValueBag( PropertyValueBagInterface $updateBag, EnvironmentInterface $environment ) { - $dataProvider = $environment->getDataProvider(); + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $sessionProperties = $this->getPropertiesFromSession($action, $environment); foreach (\array_keys($sessionProperties) as $sessionPropertyName) { @@ -311,6 +324,8 @@ private function updatePropertyValueBag( if (!$updateBag->isPropertyValueInvalid($sessionPropertyName)) { $editModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($model->getId())); + assert($editModel instanceof ModelInterface); + $updateBag->setPropertyValue( $sessionPropertyName, $editModel->getProperty($sessionPropertyName) @@ -348,8 +363,11 @@ private function handleEditHandler( PropertyValueBagInterface $propertyValueBag, EnvironmentInterface $environment ) { - $inputProvider = $this->getInputProvider($environment); + $inputProvider = $this->getInputProvider($environment); + assert($inputProvider instanceof InputProviderInterface); + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); $inputValues = $this->handleInputValues($action, $model, $environment); @@ -370,7 +388,7 @@ private function handleEditHandler( $this->restoreInputValues($action, $model, $propertyValueBag, $inputValues, $environment); $errorInformation = $editInformation->getModelError($model); - if (null !== $errorInformation) { + if ($errorInformation) { foreach (\array_keys($errorInformation) as $errorPropertyName) { if (false === $propertyValueBag->hasPropertyValue($errorPropertyName)) { continue; @@ -407,9 +425,11 @@ private function handleInputValues(Action $action, ModelInterface $model, Enviro } $inputProvider = $this->getInputProvider($environment); + assert($inputProvider instanceof InputProviderInterface); $inputValues = []; foreach (\array_keys($_POST) as $valueName) { + $valueName = (string) $valueName; $inputValues[$valueName] = $inputProvider->getValue($valueName, true); $inputProvider->unsetValue($valueName); @@ -479,11 +499,12 @@ protected function restoreInputValues( * @param Action $action The action. * @param EnvironmentInterface $environment The environment. * - * @return array + * @return string */ protected function getEditButtons(Action $action, EnvironmentInterface $environment) { $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $mode = $this->getMode($action); @@ -548,7 +569,9 @@ protected function getPropertyValueBagFromModel( EnvironmentInterface $environment ) { $propertiesDefinition = $this->getDataDefinition($environment)->getPropertiesDefinition(); - $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); $propertyValueBag = new PropertyValueBag(); @@ -594,6 +617,7 @@ protected function getCollectionFromSession(Action $action, EnvironmentInterface $sessionStorage = $this->getSessionStorage($environment); $dataDefinition = $this->getDataDefinition($environment); $dataProvider = $environment->getDataProvider($dataDefinition->getName()); + assert($dataProvider instanceof DataProviderInterface); $addEditProperties = $inputProvider->hasValue('FORM_INPUTS') @@ -608,8 +632,9 @@ protected function getCollectionFromSession(Action $action, EnvironmentInterface $modelIds[] = ModelId::fromSerialized($modelId)->getId(); if ($addEditProperties) { - $modelEditProperties = $inputProvider->getValue(\str_replace('::', '____', $modelId) . '_', true); - $inputProvider->unsetValue(\str_replace('::', '____', $modelId) . '_'); + $transformed = \str_replace('::', '____', (string) $modelId) . '_'; + $modelEditProperties = $inputProvider->getValue($transformed, true); + $inputProvider->unsetValue($transformed); $editProperties[$modelId] = $modelEditProperties; } @@ -621,6 +646,7 @@ protected function getCollectionFromSession(Action $action, EnvironmentInterface [['operation' => 'IN', 'property' => $idProperty, 'values' => $modelIds]] ) ); + assert($collection instanceof CollectionInterface); $session['editProperties'] = $editProperties; @@ -644,7 +670,7 @@ protected function getEditPropertiesByModelId( EnvironmentInterface $environment ) { $session = $this->getSession($action, $environment); -//dd($session['editProperties']); + return $session['editProperties'][$modelId->getSerialized()] ?? []; } @@ -660,8 +686,11 @@ protected function getEditPropertiesByModelId( */ protected function renderBreadcrumb(EnvironmentInterface $environment) { + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $event = new GetBreadcrumbEvent($environment); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); $elements = $event->getElements(); if (empty($elements)) { return null; @@ -692,15 +721,20 @@ protected function revertValuesByErrors( EnvironmentInterface $environment ) { $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); + if (!$editInformation->hasAnyModelError()) { return; } $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $properties = $this->getPropertiesFromSession($action, $environment); while ($collection->count() > 0) { $model = $collection->shift(); + assert($model instanceof ModelInterface); $modelErrors = $editInformation->getModelError($model); if (!$modelErrors && ('edit' === $this->getMode($action))) { @@ -708,6 +742,7 @@ protected function revertValuesByErrors( } $revertModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($model->getId())); + assert($revertModel instanceof ModelInterface); $originalModel = clone $revertModel; $revertModel->setId($revertModel->getId()); @@ -741,7 +776,11 @@ private function handlePostPersist( EnvironmentInterface $environment ) { $event = new PostPersistModelEvent($environment, $model, $originalModel); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); } /** @@ -765,6 +804,7 @@ protected function getSession(Action $action, EnvironmentInterface $environment) { $dataDefinition = $this->getDataDefinition($environment); $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getMode($action)); diff --git a/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php b/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php index 66713b4bd..71fdd7571 100644 --- a/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php +++ b/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php @@ -30,6 +30,7 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PalettesDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyTrueCondition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PaletteInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; @@ -389,7 +390,10 @@ private function invisiblePaletteProperty( continue; } - if (false === $searchProperty->getVisibleCondition()->match($intersectModel)) { + $visibleCondition = $searchProperty->getVisibleCondition(); + assert($visibleCondition instanceof PropertyConditionInterface); + + if (false === $visibleCondition->match($intersectModel)) { continue; } diff --git a/src/View/ViewTemplateInterface.php b/src/View/ViewTemplateInterface.php index 9941d11cc..7f6915985 100644 --- a/src/View/ViewTemplateInterface.php +++ b/src/View/ViewTemplateInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -29,11 +30,11 @@ interface ViewTemplateInterface /** * Set the template data from an array. * - * @param array $data The data array. + * @param array $arrData The data array. * - * @return ViewTemplateInterface + * @return self */ - public function setData($data); + public function setData($arrData); /** * Return the template data as array. @@ -48,7 +49,7 @@ public function getData(); * @param string $name Name of the value. * @param mixed $value The value to add to the template. * - * @return ViewTemplateInterface + * @return self */ public function set($name, $value); diff --git a/tests/Data/DefaultDataProviderTest.php b/tests/Data/DefaultDataProviderTest.php index 92dc82e29..80e6e966e 100644 --- a/tests/Data/DefaultDataProviderTest.php +++ b/tests/Data/DefaultDataProviderTest.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -52,7 +53,7 @@ private function mockDatabase() return $this ->getMockBuilder(Database::class) ->disableOriginalConstructor() - ->setMethods(['__destruct', 'listFields']) + ->onlyMethods(['__destruct', 'listFields']) ->getMockForAbstractClass(); } @@ -61,7 +62,7 @@ private function mockConnection() return $this ->getMockBuilder(Connection::class) ->disableOriginalConstructor() - ->setMethods(['getSchemaManager']) + ->onlyMethods(['createSchemaManager']) ->getMock(); } @@ -75,19 +76,19 @@ private function mockDefaultProvider() $schemaTable = $this ->getMockBuilder(Table::class) ->disableOriginalConstructor() - ->setMethods(['hasColumn']) + ->onlyMethods(['hasColumn']) ->getMock(); $schemaTable->method('hasColumn')->willReturn(false); $schemaManager = $this ->getMockBuilder(AbstractSchemaManager::class) ->disableOriginalConstructor() - ->setMethods(['listTableDetails']) + ->onlyMethods(['introspectTable']) ->getMockForAbstractClass(); - $schemaManager->method('listTableDetails')->willReturn($schemaTable); + $schemaManager->method('introspectTable')->willReturn($schemaTable); $connection = $this->mockConnection(); - $connection->method('getSchemaManager')->willReturn($schemaManager); + $connection->method('createSchemaManager')->willReturn($schemaManager); //$database->method('listFields')->willReturn([]); $dataProvider = new DefaultDataProvider(); @@ -122,19 +123,19 @@ public function testSetBaseConfigDeprecatedDatabase() $schemaTable = $this ->getMockBuilder(Table::class) ->disableOriginalConstructor() - ->setMethods(['hasColumn']) + ->onlyMethods(['hasColumn']) ->getMock(); $schemaTable->method('hasColumn')->willReturn(false); $schemaManager = $this ->getMockBuilder(AbstractSchemaManager::class) ->disableOriginalConstructor() - ->setMethods(['listTableDetails']) + ->onlyMethods(['introspectTable']) ->getMockForAbstractClass(); - $schemaManager->method('listTableDetails')->willReturn($schemaTable); + $schemaManager->method('introspectTable')->willReturn($schemaTable); $connection = $this->mockConnection(); - $connection->method('getSchemaManager')->willReturn($schemaManager); + $connection->method('createSchemaManager')->willReturn($schemaManager); $reflection = new \ReflectionProperty(Database::class, 'resConnection'); $reflection->setAccessible(true); @@ -160,7 +161,7 @@ public function testSetBaseConfigDeprecatedDatabase() $reflection->setAccessible(true); self::assertSame('id', $reflection->getValue($dataProvider)); - self::assertFalse($dataProvider->getTimeStampProperty()); + self::assertNull($dataProvider->getTimeStampProperty()); self::assertNull($dataProvider->getIdGenerator()); } @@ -186,24 +187,24 @@ public function testSetBaseConfigForGetDefaultConnection() $schemaTable = $this ->getMockBuilder(Table::class) ->disableOriginalConstructor() - ->setMethods(['hasColumn']) + ->onlyMethods(['hasColumn']) ->getMock(); $schemaTable->method('hasColumn')->willReturn(false); $schemaManager = $this ->getMockBuilder(AbstractSchemaManager::class) ->disableOriginalConstructor() - ->setMethods(['listTableDetails']) + ->onlyMethods(['introspectTable']) ->getMockForAbstractClass(); - $schemaManager->method('listTableDetails')->willReturn($schemaTable); + $schemaManager->method('introspectTable')->willReturn($schemaTable); $connection = $this->mockConnection(); - $connection->method('getSchemaManager')->willReturn($schemaManager); + $connection->method('createSchemaManager')->willReturn($schemaManager); $dataProvider = $this ->getMockBuilder(DefaultDataProvider::class) ->disableOriginalConstructor() - ->setMethods(['getDefaultConnection']) + ->onlyMethods(['getDefaultConnection']) ->getMock(); $dataProvider->method('getDefaultConnection')->willReturn($connection);