Skip to content

Commit a3513f9

Browse files
committed
Merge branch 'b-7.4.x-refactorings-OXDEV-8947' into b-7.4.x
2 parents 4b26381 + 59b9458 commit a3513f9

75 files changed

Lines changed: 1543 additions & 643 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.editorconfig

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# EditorConfig is awesome: https://EditorConfig.org
2+
3+
# top-most EditorConfig file
4+
root = true
5+
6+
# Unix-style newlines with a newline ending every file
7+
[*]
8+
end_of_line = lf
9+
charset = utf-8
10+
indent_style = space
11+
indent_size = 4
12+
13+
[*.php]
14+
insert_final_newline = true
15+
16+
# Unix-style newlines with a newline ending every file
17+
[{*.yml,*.yaml}]
18+
indent_size = 2

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,28 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/)
55
and this project adheres to [Semantic Versioning](http://semver.org/).
66

7+
## [v2.0.0] - Unreleased
8+
9+
### Added
10+
- Added the UserInterface with getId method to rely on
11+
- SaveGreetingRequestInterface extracted from the Service layer
12+
- Example of triggering an event after the vote action (new event - ProductVotedEvent)
13+
- TrackerInterface added to not rely on the shop model directly
14+
- Editorconfig file for more consistent coding style between devs
15+
16+
### Changed
17+
- Switch to OXID eShop 7.4 as dev dependency - updated the recipe and github workflows.
18+
- Segregate the Settings interface and moved pieces to their own domain directories:
19+
- LoggingSettingsInterface
20+
- GreetingSettingsInterface
21+
- Reworked how the active user is accessed - do not use the getUser controller method anymore.
22+
- Its better to not rely on the old controller abstractions.
23+
- Restructured the directories in Tracker - Repository and Factory goes to Infrastructure directory.
24+
- README improved with more information on the module Goals and more links to concrete example cases
25+
26+
### Fixed
27+
- More waiting time for the acceptance tests to avoid random failures.
28+
729
## [v1.0.0] - 2025-06-05
830

931
### Added

README.md

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,37 @@ Story:
5353

5454
Install and try out the module with simple examples to most common development questions.
5555

56+
We would like to encourage following ideas and principles in module development:
57+
58+
* Separate contexts into their own folders (e.g. Greeting, Tracker, ProductVote)
59+
* Basics of DDD in module development
60+
* Controllers are our use cases and entry points
61+
* Services contain business logic
62+
* Infrastructure contains shop framework/database related code
63+
* Layered architecture within those contexts (e.g. Infrastructure, Service, Controller, Subscriber)
64+
* Changing the implementation should not affect other layers
65+
* Interfaces are the contracts between layers
66+
* Data Transfer Objects (DTOs) used for data exchange between layers
67+
* Factories are responsible for creating DTOs (e.g. from database rows)
68+
* Centralizes mapping logic and makes it reusable and testable
69+
* Hexagonal architecture ideas
70+
* Ports (interfaces here) and adapters (implementations here)
71+
* Adapters depend on ports, not the other way round
72+
* Application core is independent of external systems (as much as possible in the current context)
73+
* Dependency injection
74+
* Allows awesome testability and flexibility
75+
* Avoids usage of global state (Registry, oxNew, static calls)
76+
* Use factories to create model objects instead of oxNew directly
77+
* SOLID principles
78+
* SRP and DIP in focus
79+
* Clean architecture ideas
80+
* Dependencies point inwards
81+
* Business logic is independent of frameworks, databases, UIs
82+
* Avoid the extension of shop core classes as much as possible
83+
* Prefer event listeners, DI service decoration/replacement
84+
* If extension is necessary, follow minimal invasion principle
85+
* Tests are a good example of Unit usage
86+
5687
## Examples
5788

5889
The repository contains examples of following cases and more:
@@ -61,44 +92,53 @@ The repository contains examples of following cases and more:
6192
* extending a shop model (`OxidEsales\ExamplesModule\Extension\Model\User`) / (`OxidEsales\ExamplesModule\Extension\Model\Basket`)
6293
* extending a shop controller (`OxidEsales\ExamplesModule\Extension\Controller\StartController`)
6394

64-
* [Controllers as service](https://github.com/OXID-eSales/examples-module/blob/b-7.4.x/src/Greeting/services.yaml#L28)
95+
* [Controllers as service](https://github.com/OXID-eSales/examples-module/blob/b-7.4.x/src/Greeting/services.yaml#L29)
6596
* own module controller (`oeem_greeting` with own template and own translations)
6697
* own module admin controller (`oeem_admin_greeting` with own template and own translations)
6798

68-
* [Using Symfony DI](https://github.com/OXID-eSales/examples-module/blob/b-7.4.x/services.yaml)
69-
* [Injection of Registry classes with bind](https://github.com/OXID-eSales/examples-module/blob/b-7.4.x/src/Greeting/services.yaml#L5)
99+
* [Using Symfony DI](services.yaml)
100+
* [Injection of Registry classes with bind](https://github.com/OXID-eSales/examples-module/blob/b-7.4.x/services.yaml#L16)
101+
* [Service decoration](src/Greeting/Service/Decorator/GreetingValidationDecorator.php) - shows how to decorate services
102+
* Note: While the example uses validation/truncation for simplicity, better use cases include logging, caching, performance monitoring, or audit trails
103+
* [Decorator registration](src/Greeting/services.yaml) - using `decorates:` in DI configuration
70104

71-
* [Migrations](https://github.com/OXID-eSales/examples-module/tree/b-7.4.x/migration)
105+
* [Migrations](migration)
72106
* extending a shop database table (`oxuser`)
73107

74108
* Accessing the database
75-
* model with a database (`OxidEsales\ExamplesModule\Tracker\Model\GreetingTracker`)
76-
* ``oxNew`` object factory example (`OxidEsales\ExamplesModule\Greeting\Infrastructure\UserModelFactory`)
77-
* [DAO](src/ProductVote/Dao)
109+
* Model with a database (`OxidEsales\ExamplesModule\Tracker\Model\TrackerModel`)
110+
* ``oxNew`` object factory example (`OxidEsales\ExamplesModule\Greeting\Infrastructure\Factory\UserModelFactory`)
111+
* [DAO examples](src/ProductVote/Dao) - lower level abstraction for database access
112+
* Repository examples - higher level abstraction for data access
113+
* [GreetingRepository](src/Greeting/Infrastructure/Repository/GreetingRepository.php) - direct database query with QueryBuilder example (consider making DAO instead for cases like this)
114+
* [UserRepository](src/Greeting/Infrastructure/Repository/UserRepository.php) - loading shop user with model example
115+
* [TrackerRepository](src/Tracker/Infrastructure/Repository/TrackerRepository.php) - more comprehensive example showing dependencies and DTO usage
78116

79117
* [Various types of module settings](https://github.com/OXID-eSales/examples-module/blob/b-7.4.x/metadata.php#L38)
80118

81119
* Templates
82-
* [creating templates for your module](https://github.com/OXID-eSales/examples-module/blob/b-7.4.x/views/twig/templates/greetingtemplate.html.twig)
83-
* [extending of oxid theme templates or blocks](https://github.com/OXID-eSales/examples-module/tree/b-7.4.x/views/twig/extensions/themes)
120+
* [creating templates for your module](views/twig/templates/greetingtemplate.html.twig)
121+
* [extending of oxid theme templates or blocks](views/twig/extensions/themes)
84122
* extending a shop admin template block (`admin_user_main_form` - only an extension of a block, without functionality)
85123
* extending a shop template block (`start_newest_articles`)
86124

87125
* Using the translations for your module specific phrases
88-
* [in admin](https://github.com/OXID-eSales/examples-module/tree/b-7.4.x/views/admin_twig)
89-
* [in frontend](https://github.com/OXID-eSales/examples-module/tree/b-7.4.x/translations)
126+
* [in admin](views/admin_twig)
127+
* [in frontend](translations)
90128

91129
* Events and listeners
92-
* [Subscribing to shop events](https://github.com/OXID-eSales/examples-module/blob/b-7.4.x/src/Tracker/Subscriber/BeforeModelUpdate.php)
130+
* [Subscribing to shop events](src/Tracker/Subscriber/BeforeModelUpdate.php) - listening to `BeforeModelUpdateEvent`
131+
* [Creating and dispatching custom module events](src/ProductVote/Event/ProductVotedEvent.php)
132+
* [Dispatching the event](src/ProductVote/Service/VoteService.php) - triggering events from services
93133

94134
* Testing your module backend and frontend part
95135
* [Composer aliases for easy running of tests and quality tools](https://github.com/OXID-eSales/examples-module/blob/b-7.4.x/composer.json#L48)
96-
* [Using the github actions as CI tool with all recommended tools preconfigured for you.](https://github.com/OXID-eSales/examples-module/tree/b-7.4.x/.github)
136+
* [Using the github actions as CI tool with all recommended tools preconfigured for you.](.github)
97137

98-
* [Using variables from .env file](https://github.com/OXID-eSales/examples-module/tree/b-7.4.x/.env)
99-
* [Access via `getenv()` function](https://github.com/OXID-eSales/examples-module/tree/b-7.4.x/src/Extension/Controller/StartController.php)
138+
* [Using variables from .env file](.env)
139+
* [Access via `getenv()` function](src/Extension/Controller/StartController.php)
100140
* Note: Changes to environment variables take effect immediately — no cache clearing is required.
101-
* [Access via DI container](https://github.com/OXID-eSales/examples-module/tree/b-7.4.x/src/Greeting/services.yaml)
141+
* [Access via DI container](src/Greeting/services.yaml)
102142
* Note: After updating environment variables, you must clear the cache for changes to take effect.
103143

104144
**HINTS**:

services.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@ imports:
66
- { resource: src/Greeting/services.yaml }
77
- { resource: src/Logging/services.yaml }
88
- { resource: src/ProductVote/services.yaml }
9-
- { resource: src/Settings/services.yaml }
109
- { resource: src/Tracker/services.yaml }
1110

1211
services:
1312

1413
_defaults:
1514
public: false
1615
autowire: true
16+
# This is how you can bind shop registry services to your services
17+
# bind:
18+
# OxidEsales\Eshop\Core\Language: '@=service("OxidEsales\\ExamplesModule\\Core\\Registry").getLang()'
19+
# OxidEsales\Eshop\Core\Session: '@=service("OxidEsales\\ExamplesModule\\Core\\Registry").getSession()'
1720

1821
OxidEsales\ExamplesModule\Core\Registry:
1922
class: OxidEsales\Eshop\Core\Registry

src/Extension/Controller/StartController.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
use OxidEsales\Eshop\Application\Model\User as EshopModelUser;
1313
use OxidEsales\ExamplesModule\Greeting\Service\GreetingMessageServiceInterface;
14-
use OxidEsales\ExamplesModule\Settings\Service\ModuleSettingsServiceInterface;
14+
use OxidEsales\ExamplesModule\Greeting\Settings\GreetingSettingsInterface;
1515

1616
/**
1717
* @eshopExtension
@@ -55,9 +55,9 @@ public function getOeemGreeting(): string
5555

5656
public function canUpdateOeemGreeting(): bool
5757
{
58-
$moduleSettings = $this->getService(ModuleSettingsServiceInterface::class);
58+
$greetingSettings = $this->getService(GreetingSettingsInterface::class);
5959

6060
/** @phpstan-ignore-next-line */
61-
return is_a($this->getUser(), EshopModelUser::class) && $moduleSettings->isPersonalGreetingMode();
61+
return is_a($this->getUser(), EshopModelUser::class) && $greetingSettings->isPersonalGreetingMode();
6262
}
6363
}

src/Extension/Model/User.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
use OxidEsales\Eshop\Core\Model\BaseModel;
1313
use OxidEsales\ExamplesModule\Greeting\Model\PersonalGreetingUser;
14-
use OxidEsales\ExamplesModule\Greeting\Model\PersonalGreetingUserInterface;
1514

1615
/**
1716
* @eshopExtension
@@ -28,4 +27,9 @@
2827
class User extends User_parent implements UserInterface
2928
{
3029
use PersonalGreetingUser;
30+
31+
public function getId(): ?string
32+
{
33+
return parent::getId();
34+
}
3135
}

src/Extension/Model/UserInterface.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@
1111

1212
interface UserInterface extends PersonalGreetingUserInterface
1313
{
14+
public function getId(): ?string;
1415
}

src/Greeting/Controller/GreetingController.php

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
namespace OxidEsales\ExamplesModule\Greeting\Controller;
1111

1212
use OxidEsales\Eshop\Application\Controller\FrontendController;
13-
use OxidEsales\Eshop\Application\Model\User as EshopModelUser;
1413
use OxidEsales\ExamplesModule\Core\Module as ModuleCore;
15-
use OxidEsales\ExamplesModule\Extension\Model\User as ExamplesModelUser;
14+
use OxidEsales\ExamplesModule\Extension\Model\UserInterface;
15+
use OxidEsales\ExamplesModule\Greeting\Exception\UserNotLoggedIn;
16+
use OxidEsales\ExamplesModule\Greeting\Infrastructure\Repository\UserRepositoryInterface;
1617
use OxidEsales\ExamplesModule\Greeting\Service\GreetingMessageServiceInterface;
17-
use OxidEsales\ExamplesModule\Settings\Service\ModuleSettingsServiceInterface;
18-
use OxidEsales\ExamplesModule\Tracker\Repository\TrackerRepositoryInterface;
18+
use OxidEsales\ExamplesModule\Greeting\Settings\GreetingSettingsInterface;
19+
use OxidEsales\ExamplesModule\Greeting\Transput\SaveGreetingRequestInterface;
20+
use OxidEsales\ExamplesModule\Tracker\Infrastructure\Repository\TrackerRepositoryInterface;
1921

2022
/**
2123
* @extendable-class
@@ -34,9 +36,11 @@ class GreetingController extends FrontendController
3436
protected $_sThisTemplate = '@oe_examples_module/templates/greetingtemplate';
3537

3638
public function __construct(
37-
private readonly ModuleSettingsServiceInterface $moduleSettings,
39+
private readonly GreetingSettingsInterface $greetingSettings,
3840
private readonly TrackerRepositoryInterface $trackerRepository,
3941
private readonly GreetingMessageServiceInterface $greetingService,
42+
private readonly SaveGreetingRequestInterface $saveGreetingRequest,
43+
private readonly UserRepositoryInterface $userRepository,
4044
) {
4145
parent::__construct();
4246
}
@@ -50,13 +54,19 @@ public function render()
5054
{
5155
$template = parent::render();
5256

53-
/** @var ExamplesModelUser $user */
54-
$user = $this->getUser();
57+
try {
58+
$activeUser = $this->userRepository->getActiveUser();
59+
} catch (UserNotLoggedIn $e) {
60+
$activeUser = null;
61+
}
5562

56-
/** @phpstan-ignore-next-line */
57-
if (is_a($user, EshopModelUser::class) && $this->moduleSettings->isPersonalGreetingMode()) {
58-
$greeting = $user->getPersonalGreeting();
59-
$tracker = $this->trackerRepository->getTrackerByUserId($user->getId());
63+
if (
64+
$activeUser instanceof UserInterface
65+
&& !empty($activeUser->getId())
66+
&& $this->greetingSettings->isPersonalGreetingMode()
67+
) {
68+
$greeting = $activeUser->getPersonalGreeting();
69+
$tracker = $this->trackerRepository->getTrackerByUserId($activeUser->getId());
6070
$counter = $tracker->getCount();
6171
}
6272

@@ -74,12 +84,8 @@ public function render()
7484
*/
7585
public function updateGreeting(): void
7686
{
77-
/** @var EshopModelUser $user */
78-
$user = $this->getUser();
79-
80-
/** @phpstan-ignore-next-line */
81-
if (is_a($user, EshopModelUser::class) && $this->moduleSettings->isPersonalGreetingMode()) {
82-
$this->greetingService->saveGreeting($user);
87+
if ($this->greetingSettings->isPersonalGreetingMode()) {
88+
$this->greetingService->saveGreetingForCurrentUser($this->saveGreetingRequest->getGreetingMessage());
8389
}
8490
}
8591
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
/**
4+
* Copyright © OXID eSales AG. All rights reserved.
5+
* See LICENSE file for license details.
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace OxidEsales\ExamplesModule\Greeting\Exception;
11+
12+
class UserNotLoggedIn extends \Exception
13+
{
14+
}

src/Greeting/Infrastructure/UserModelFactory.php renamed to src/Greeting/Infrastructure/Factory/UserModelFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
declare(strict_types=1);
99

10-
namespace OxidEsales\ExamplesModule\Greeting\Infrastructure;
10+
namespace OxidEsales\ExamplesModule\Greeting\Infrastructure\Factory;
1111

1212
use OxidEsales\Eshop\Application\Model\User;
1313

0 commit comments

Comments
 (0)