Skip to content

Commit 3e24a36

Browse files
authored
Merge pull request PrestaShop#2152 from hadjedjvincent/services-php-config
Explain services.php usage in modules
2 parents 2b8e8b3 + a8c9fe3 commit 3e24a36

6 files changed

Lines changed: 106 additions & 35 deletions

File tree

modules/concepts/commands.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ class ExportCommand extends Command
6666

6767
Now, in order to make this really simple command available in the console, we register it in the services.yml file:
6868

69+
This example uses YAML, but other service configuration files are supported. See [Services]({{< relref "/9/modules/concepts/services/" >}}) for more details.
70+
6971
```yaml
7072
# your-module/config/services.yml
7173
services:

modules/concepts/controllers/admin-controllers/_index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ public function __construct(
136136

137137
In PrestaShop 9.0, controllers must be defined as services. You have two main approaches to configure your controller service:
138138

139+
The following examples use YAML, but other service configuration files are supported. See [Services]({{< relref "/9/modules/concepts/services/" >}}) for more details.
140+
139141
### Option 1: Explicit service configuration with tags
140142

141143
```yaml

modules/concepts/hooks/use-hooks-on-modern-pages.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,9 @@ services:
152152
Note: Since Symfony 4.4, services that are not dependency injected and that are not declared as “public” are removed from the container.
153153
{{% /notice %}}
154154
155-
Prestashop automatically checks if modules have a `config/services.yml` file and will autoload it for you. In order to force Prestashop to parse the file, you need to clear the cache:
155+
PrestaShop automatically checks module service configuration files and will autoload them for you. This example uses YAML, but other service configuration files are supported. See [Services]({{< relref "/9/modules/concepts/services/" >}}) for more details.
156+
157+
In order to force PrestaShop to parse the file, you need to clear the cache:
156158
157159
```
158160
./bin/console cache:clear --no-warmup
@@ -289,4 +291,3 @@ We have used a key for translation, making our own translations available in bac
289291
And "voila!", the module could be of course improved with so many features, adding filters on export for instance, using the `request` hook parameter and updating the Product repository.
290292

291293
[setup-composer]: {{< ref "/9/modules/concepts/composer.md" >}}
292-

modules/concepts/services/_index.md

Lines changed: 80 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class YourService {
5757
}
5858
```
5959

60-
Now that your namespace is setup, you can define your services in the `config/services.yml` file of your module.
60+
Now that your namespace is setup, you can define your services in a service configuration file of your module, for example `config/services.yml`.
6161

6262
```yml
6363
# yourmodule/config/services.yml
@@ -72,24 +72,75 @@ services:
7272
- "My custom message"
7373
```
7474
75-
{{% notice tip %}} It is possible to load PHP / XML files for modules services{{% /notice %}}
75+
#### Service configuration files
76+
{{< minver v="9.2.0" title="true" >}}
7677
77-
```yml
78-
# yourmodule/config/services.yml
79-
imports:
80-
- { resource: services.php }
78+
Starting from PrestaShop 9.2, modules can use several service configuration files. PrestaShop loads the first existing
79+
file in this order:
80+
81+
1. `services.php`
82+
2. `services-{major}.{minor}.yml`, for example `services-9.2.yml`
83+
3. `services-{major}.yml`, for example `services-9.yml`
84+
4. `services.yml`
85+
86+
The same priority applies to the supported module service configuration folders, such as `config/`, `config/admin/`,
87+
`config/front/` and `config/webservice/`.
88+
89+
This means you can use PHP service configuration in PrestaShop 9.2 and newer:
90+
91+
```php
92+
<?php
93+
// yourmodule/config/services.php
94+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
95+
96+
return static function (ContainerConfigurator $container): void {
97+
$services = $container->services();
98+
99+
$services
100+
->defaults()
101+
->public();
102+
103+
$services
104+
->set('your_company.your_module.your_service', \YourCompany\YourModule\YourService::class)
105+
->args([
106+
service('translator'),
107+
'My custom message',
108+
]);
109+
};
81110
```
82111

112+
You can use `services.php` as an entry point to load different service definitions depending on the PrestaShop version:
113+
83114
```php
84115
<?php
85116
// yourmodule/config/services.php
86117
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
87118
88-
return function(ContainerConfigurator $configurator) {
119+
return static function (ContainerConfigurator $container): void {
120+
if (version_compare(_PS_VERSION_, '9.2.0', '>=')) {
121+
$container->import('services/services-ps-9.2.php');
122+
123+
return;
124+
}
89125
126+
$container->import('services/services-ps-9.php');
90127
};
91128
```
92129

130+
You can also keep YAML files as fallbacks for compatibility with older PrestaShop versions:
131+
132+
```text
133+
yourmodule/
134+
`-- config/
135+
|-- services.php
136+
|-- services-9.2.yml
137+
|-- services-9.yml
138+
`-- services.yml
139+
```
140+
141+
When `services.php` exists, it has priority. Otherwise PrestaShop falls back to the most specific YAML file matching
142+
the current PrestaShop version, then to `services.yml`.
143+
93144
This will then allow you to get your service from the Symfony container, like in your modern controllers:
94145

95146
```php
@@ -114,7 +165,7 @@ class DemoController extends FrameworkBundleAdminController
114165

115166
{{% notice tip %}}
116167
If you need more details about dependency injection and how services work in the Symfony environment we recommend you to read
117-
their documentation about the [Service Container](https://symfony.com/doc/4.4/service_container.html).
168+
their documentation about the [Service Container](https://symfony.com/doc/6.4/service_container.html).
118169
{{% /notice %}}
119170

120171
##### Exclude index.php files when adding wildcard resource
@@ -135,13 +186,13 @@ The container definition can be modified by a module, which enables you to overr
135186

136187
This is a mechanism similar to PrestaShop standard overrides, but the main benefit is that the php code stays unmodified. This prevents issues linked to code definition or autoloading failures.
137188

138-
As you can read it from the [Symfony documentation](https://symfony.com/doc/current/service_container/service_decoration.html), there are 2 ways to modify an existing service:
189+
As you can read it from the [Symfony documentation](https://symfony.com/doc/6.4/service_container/service_decoration.html), there are 2 ways to modify an existing service:
139190

140191
#### Override the service
141192

142193
When you choose to override a service, this means that you _replace the service by another one_. The previous service is not usable anymore. Every other part of the code where this service is used will use the new version.
143194

144-
To do it: you declare your new service using the old service name. So if you want to override the service `prestashop.core.b2b.b2b_feature` with your own implementation, you write in `config/services.yml` :
195+
To do it: you declare your new service using the old service name. So if you want to override the service `prestashop.core.b2b.b2b_feature` with your own implementation, you can write for example in `config/services.yml`:
145196

146197
```yml
147198
prestashop.core.b2b.b2b_feature:
@@ -154,7 +205,7 @@ That's done. The service registered under the name `prestashop.core.b2b.b2b_feat
154205

155206
When you choose to decorate a service, this means that you _make everybody use your service but you keep the old service available_. The previous service has been given a new name and can still be used. Every other part of the code where this service was used will use the new version.
156207

157-
To do it: you declare your new service using the 'decorates' keyword. So if you want to decorates the service `prestashop.core.b2b.b2b_feature` with my own implementation, you write in `config/services.yml` :
208+
To do it: you declare your new service using the 'decorates' keyword. So if you want to decorates the service `prestashop.core.b2b.b2b_feature` with my own implementation, you can write for example in `config/services.yml`:
158209

159210
```yml
160211
mymodule.my_own_b2b_feature_service:
@@ -167,7 +218,7 @@ That's done. The service registered under the name `mymodule.my_own_b2b_feature_
167218
This means that in your container you can access 3 services now:
168219

169220
- `mymodule.my_own_b2b_feature_service` your service
170-
- `prestashop.core.b2b.b2b_feature` is now an alias for `mymodule.my_own_b2b_feature_service` (see [service aliases](https://symfony.com/doc/current/service_container/alias_private.html)) so the other services which rely on it now use your implementation
221+
- `prestashop.core.b2b.b2b_feature` is now an alias for `mymodule.my_own_b2b_feature_service` (see [service aliases](https://symfony.com/doc/6.4/service_container/alias_private.html)) so the other services which rely on it now use your implementation
171222
- `mymodule.my_own_b2b_feature_service.inner` is the previous implementation, still available
172223

173224
The decoration strategy can be very useful if:
@@ -257,7 +308,7 @@ As you can see, interfaces lay the ground for easy extension and customization,
257308

258309
#### Advanced services parameters (_instanceof or interface binding, manual tags)
259310

260-
Since {{< minver v=8.1 >}}, [modules autoloaders and service configurations loading are now registered before compiler passes](https://github.com/PrestaShop/PrestaShop/pull/30588). That means that you can now use native Symfony service configuration features in your modules.
311+
Since {{< minver v=8.1 >}}, [modules autoloaders and service configurations loading are now registered before compiler passes](https://github.com/PrestaShop/PrestaShop/pull/30588). That means that you can now use native Symfony service configuration features in your modules.
261312

262313
Those features are:
263314

@@ -266,7 +317,7 @@ Those features are:
266317
- [an option to skip the class attribute](https://symfony.com/blog/new-in-symfony-3-3-optional-class-for-named-services)
267318
- [automatically registering classes found in the specified directories as services](https://symfony.com/blog/new-in-symfony-3-3-psr-4-based-service-discovery)
268319

269-
As an example, let's consider a module with the following structure:
320+
As an example, let's consider a module with the following structure:
270321

271322
```
272323
config/
@@ -278,9 +329,9 @@ src/
278329
ElementInterface.php
279330
```
280331

281-
And this content:
332+
And this content:
282333

283-
File: `src/Collection/Collection.php`
334+
File: `src/Collection/Collection.php`
284335

285336
```php
286337
<?php
@@ -339,7 +390,7 @@ services:
339390
tags: [ test_module.instance_of.instance_of_tagged ]
340391
```
341392

342-
This example will tag all classes _instances of_ `TestModule\InstanceofConditionals\Collection\ElementInterface` (`TestModule\InstanceofConditionals\Collection\Element` in our example) with the tag `test_module.instance_of.instance_of_tagged`.
393+
This example will tag all classes _instances of_ `TestModule\InstanceofConditionals\Collection\ElementInterface` (`TestModule\InstanceofConditionals\Collection\Element` in our example) with the tag `test_module.instance_of.instance_of_tagged`.
343394

344395
Then, it will bind all services with a `$element` variable in its constructor with a `test_module.instance_of.instance_of_tagged` service.
345396

@@ -363,7 +414,7 @@ This example will tag the class `TestModule\InstanceofConditionals\Collection\El
363414

364415
Then, it will bind all services with a `$element` variable in its constructor with a `test_module.instance_of.manually_tagged` service.
365416

366-
If we wanted to bind only parameter `$element` of class `TestModule\InstanceofConditionals\Collection\Collection` with a `test_module.instance_of.manually_tagged` tag, we would had configured it this way:
417+
If we wanted to bind only parameter `$element` of class `TestModule\InstanceofConditionals\Collection\Collection` with a `test_module.instance_of.manually_tagged` tag, we would had configured it this way:
367418

368419
```yaml
369420
services:
@@ -378,7 +429,7 @@ services:
378429
tags: [ test_module.instance_of.manually_tagged ]
379430
```
380431

381-
Explore more configuration features in [the official Symfony documentation](https://symfony.com/doc/4.4/service_container.html#creating-configuring-services-in-the-container).
432+
Explore more configuration features in [the official Symfony documentation](https://symfony.com/doc/6.4/service_container.html#creating-configuring-services-in-the-container).
382433

383434
## Services in Legacy environment
384435
{{< minver v="1.7.6" title="true" >}}
@@ -393,8 +444,10 @@ container for this environment (`PrestaShop\PrestaShop\Adapter\ContainerBuilder`
393444
To define your services you need to follow the same principle as Symfony services, but this time you need to place your definition
394445
files in sub folders:
395446

396-
- `config/admin/services.yml` will define the services accessible in the back office (in legacy environment AND Symfony environment)
397-
- `config/front/services.yml` will define the services accessible in the front office
447+
- `config/admin/` service configuration files will define the services accessible in the back office (in legacy environment AND Symfony environment)
448+
- `config/front/` service configuration files will define the services accessible in the front office
449+
450+
The service file priority described above also applies in these folders.
398451

399452
{{% notice warning %}}
400453
**Do not use named arguments for front services definition**
@@ -467,12 +520,12 @@ in admin or front. Be careful and always keep in mind in which context/environme
467520

468521
Here is a quick summary so that you know where you should define your services:
469522

470-
| Definition file | Symfony Container | Front Legacy Container | Admin Legacy Container | Webservice Container | Available services |
471-
| --------------------------- | :---------------: | :--------------------: | :--------------------: | :------------------: | -------------------------------------------------------------------------- |
472-
| `config/services.yml` | Yes | No | No | No | All Symfony components and `PrestaShopBundle` services |
473-
| `config/admin/services.yml` | Yes | No | Yes | No | Doctrine, services defined in `<PS_ROOT_DIR>/config/services/admin` folder |
474-
| `config/front/services.yml` | Yes | Yes | No | No | Doctrine, services defined in `<PS_ROOT_DIR>/config/services/front` folder |
475-
| `config/webservice/services.yml` | Yes | No | No | Yes | Doctrine, services defined in `<PS_ROOT_DIR>/config/webservice/front` folder |
523+
| Definition folder | Symfony Container | Front Legacy Container | Admin Legacy Container | Webservice Container | Available services |
524+
| ----------------------- | :---------------: | :--------------------: | :--------------------: | :------------------: | -------------------------------------------------------------------------- |
525+
| `config/` | Yes | No | No | No | All Symfony components and `PrestaShopBundle` services |
526+
| `config/admin/` | Yes | No | Yes | No | Doctrine, services defined in `<PS_ROOT_DIR>/config/services/admin` folder |
527+
| `config/front/` | Yes | Yes | No | No | Doctrine, services defined in `<PS_ROOT_DIR>/config/services/front` folder |
528+
| `config/webservice/` | Yes | No | No | Yes | Doctrine, services defined in `<PS_ROOT_DIR>/config/webservice/front` folder |
476529

477530

478531
### Define a service on both front and admin

modules/creation/adding-configuration-page-modern.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ This form has only one setting : `config_text`, of type `Symfony\Component\Form\
114114

115115
### Register your newly created form type
116116

117-
Create a `services.yml` file in `config/`.
117+
Create a `services.yml` file in `config/`. This example uses YAML, but other service configuration files are supported. See [Services]({{< relref "/9/modules/concepts/services/" >}}) for more details.
118118

119119
```yml
120120
services:

modules/creation/module-file-structure.md

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ weight: 2
66
# Module file structure
77

88
A module is made of a lot of files, all stored in a folder that bears the same name as the module, that folder being in turn stored in the
9-
`/modules` folder at the root of the main PrestaShop folder: `/modules/<modulename>/`.
9+
`/modules` folder at the root of the main PrestaShop folder: `/modules/<modulename>/`.
1010

1111
{{% notice tip %}}
1212
Your module can be called anything, as long as it only contains lowercase letters and numbers (`/[a-z0-9]/`).
@@ -17,15 +17,24 @@ A module distributed in a zip archive file must also be placed in a subfolder wi
1717

1818
## Main files and directories
1919

20-
Here are an example of files and folders for a PrestaShop 1.7 module:
20+
Here is an example of files and folders for a PrestaShop 9.2 module:
2121

2222
```
2323
mymodule
2424
├── config
2525
│ ├── admin
26-
│ │ └── services.yml
26+
│ │ ├── services.php
27+
│ │ ├── services-9.2.yml
28+
│ │ ├── services-9.yml
29+
│ │ └── services.yml
2730
│ ├── front
31+
│ │ ├── services.php
32+
│ │ ├── services-9.2.yml
33+
│ │ ├── services-9.yml
2834
│ │ └── services.yml
35+
│ ├── services.php
36+
│ ├── services-9.2.yml
37+
│ ├── services-9.yml
2938
│ └── services.yml
3039
├── controllers
3140
├── override
@@ -52,6 +61,10 @@ Let's go through each one of the above.
5261

5362
The `config` folder is the place where configuration files are stored. In particular, [Routes][sf-routes] and [Services][sf-services].
5463

64+
Starting from PrestaShop 9.2, module service configuration files can use several names and formats, as shown in the
65+
example above. Only one service configuration file is loaded per folder. See [Services][sf-services] for more details
66+
about service configuration files and their loading priority.
67+
5568
### `controllers/` folder
5669

5770
The `controllers` folder contains the legacy-style Controller files.
@@ -69,7 +82,7 @@ Symfony-based controllers go in the ["`src`" folder](#src-folder), described bel
6982

7083
### `override/` folder
7184

72-
PHP files placed in the `override` folder will replace the ones from the Core.
85+
PHP files placed in the `override` folder will replace the ones from the Core.
7386

7487
{{% notice warning %}}
7588
Overrides is a powerful, yet risky feature. Avoid using it if you can.
@@ -149,7 +162,7 @@ This icon file will be displayed in module listings if present. It needs to be a
149162

150163
### `mymodule.php` file (main file)
151164

152-
The module's main PHP file should be named the same as the module’s root folder.
165+
The module's main PHP file should be named the same as the module’s root folder.
153166

154167
Example for the BlockCMS module:
155168

0 commit comments

Comments
 (0)