Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions modules/concepts/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class ExportCommand extends Command

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

This example uses YAML, but other service configuration files are supported. See [Services]({{< relref "/9/modules/concepts/services/" >}}) for more details.

```yaml
# your-module/config/services.yml
services:
Expand Down
2 changes: 2 additions & 0 deletions modules/concepts/controllers/admin-controllers/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ public function __construct(

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

The following examples use YAML, but other service configuration files are supported. See [Services]({{< relref "/9/modules/concepts/services/" >}}) for more details.

### Option 1: Explicit service configuration with tags

```yaml
Expand Down
5 changes: 3 additions & 2 deletions modules/concepts/hooks/use-hooks-on-modern-pages.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,9 @@ services:
Note: Since Symfony 4.4, services that are not dependency injected and that are not declared as “public” are removed from the container.
{{% /notice %}}

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:
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.

In order to force PrestaShop to parse the file, you need to clear the cache:

```
./bin/console cache:clear --no-warmup
Expand Down Expand Up @@ -289,4 +291,3 @@ We have used a key for translation, making our own translations available in bac
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.

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

107 changes: 80 additions & 27 deletions modules/concepts/services/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class YourService {
}
```

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

```yml
# yourmodule/config/services.yml
Expand All @@ -72,24 +72,75 @@ services:
- "My custom message"
```

{{% notice tip %}} It is possible to load PHP / XML files for modules services{{% /notice %}}
#### Service configuration files
{{< minver v="9.2.0" title="true" >}}

```yml
# yourmodule/config/services.yml
imports:
- { resource: services.php }
Starting from PrestaShop 9.2, modules can use several service configuration files. PrestaShop loads the first existing
file in this order:

1. `services.php`
2. `services-{major}.{minor}.yml`, for example `services-9.2.yml`
3. `services-{major}.yml`, for example `services-9.yml`
4. `services.yml`

The same priority applies to the supported module service configuration folders, such as `config/`, `config/admin/`,
`config/front/` and `config/webservice/`.

This means you can use PHP service configuration in PrestaShop 9.2 and newer:

```php
<?php
// yourmodule/config/services.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

return static function (ContainerConfigurator $container): void {
$services = $container->services();

$services
->defaults()
->public();

$services
->set('your_company.your_module.your_service', \YourCompany\YourModule\YourService::class)
->args([
service('translator'),
'My custom message',
]);
};
```

You can use `services.php` as an entry point to load different service definitions depending on the PrestaShop version:

```php
<?php
// yourmodule/config/services.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

return function(ContainerConfigurator $configurator) {
return static function (ContainerConfigurator $container): void {
if (version_compare(_PS_VERSION_, '9.2.0', '>=')) {
$container->import('services/services-ps-9.2.php');

return;
}

$container->import('services/services-ps-9.php');
};
```

You can also keep YAML files as fallbacks for compatibility with older PrestaShop versions:

```text
yourmodule/
`-- config/
|-- services.php
|-- services-9.2.yml
|-- services-9.yml
`-- services.yml
```

When `services.php` exists, it has priority. Otherwise PrestaShop falls back to the most specific YAML file matching
the current PrestaShop version, then to `services.yml`.

This will then allow you to get your service from the Symfony container, like in your modern controllers:

```php
Expand All @@ -114,7 +165,7 @@ class DemoController extends FrameworkBundleAdminController

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

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

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.

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:
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:

#### Override the service

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.

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` :
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`:

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

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.

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` :
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`:

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

- `mymodule.my_own_b2b_feature_service` your service
- `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
- `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
- `mymodule.my_own_b2b_feature_service.inner` is the previous implementation, still available

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

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

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.
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.

Those features are:

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

As an example, let's consider a module with the following structure:
As an example, let's consider a module with the following structure:

```
config/
Expand All @@ -278,9 +329,9 @@ src/
ElementInterface.php
```

And this content:
And this content:

File: `src/Collection/Collection.php`
File: `src/Collection/Collection.php`

```php
<?php
Expand Down Expand Up @@ -339,7 +390,7 @@ services:
tags: [ test_module.instance_of.instance_of_tagged ]
```

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`.
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`.

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

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

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

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:
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:

```yaml
services:
Expand All @@ -378,7 +429,7 @@ services:
tags: [ test_module.instance_of.manually_tagged ]
```

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

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

- `config/admin/services.yml` will define the services accessible in the back office (in legacy environment AND Symfony environment)
- `config/front/services.yml` will define the services accessible in the front office
- `config/admin/` service configuration files will define the services accessible in the back office (in legacy environment AND Symfony environment)
- `config/front/` service configuration files will define the services accessible in the front office

The service file priority described above also applies in these folders.

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

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

| Definition file | Symfony Container | Front Legacy Container | Admin Legacy Container | Webservice Container | Available services |
| --------------------------- | :---------------: | :--------------------: | :--------------------: | :------------------: | -------------------------------------------------------------------------- |
| `config/services.yml` | Yes | No | No | No | All Symfony components and `PrestaShopBundle` services |
| `config/admin/services.yml` | Yes | No | Yes | No | Doctrine, services defined in `<PS_ROOT_DIR>/config/services/admin` folder |
| `config/front/services.yml` | Yes | Yes | No | No | Doctrine, services defined in `<PS_ROOT_DIR>/config/services/front` folder |
| `config/webservice/services.yml` | Yes | No | No | Yes | Doctrine, services defined in `<PS_ROOT_DIR>/config/webservice/front` folder |
| Definition folder | Symfony Container | Front Legacy Container | Admin Legacy Container | Webservice Container | Available services |
| ----------------------- | :---------------: | :--------------------: | :--------------------: | :------------------: | -------------------------------------------------------------------------- |
| `config/` | Yes | No | No | No | All Symfony components and `PrestaShopBundle` services |
| `config/admin/` | Yes | No | Yes | No | Doctrine, services defined in `<PS_ROOT_DIR>/config/services/admin` folder |
| `config/front/` | Yes | Yes | No | No | Doctrine, services defined in `<PS_ROOT_DIR>/config/services/front` folder |
| `config/webservice/` | Yes | No | No | Yes | Doctrine, services defined in `<PS_ROOT_DIR>/config/webservice/front` folder |


### Define a service on both front and admin
Expand Down
2 changes: 1 addition & 1 deletion modules/creation/adding-configuration-page-modern.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ This form has only one setting : `config_text`, of type `Symfony\Component\Form\

### Register your newly created form type

Create a `services.yml` file in `config/`.
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.

```yml
services:
Expand Down
23 changes: 18 additions & 5 deletions modules/creation/module-file-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ weight: 2
# Module file structure

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
`/modules` folder at the root of the main PrestaShop folder: `/modules/<modulename>/`.
`/modules` folder at the root of the main PrestaShop folder: `/modules/<modulename>/`.

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

## Main files and directories

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

```
mymodule
├── config
│ ├── admin
│ │ └── services.yml
│ │ ├── services.php
│ │ ├── services-9.2.yml
│ │ ├── services-9.yml
│ │ └── services.yml
│ ├── front
│ │ ├── services.php
│ │ ├── services-9.2.yml
│ │ ├── services-9.yml
│ │ └── services.yml
│ ├── services.php
│ ├── services-9.2.yml
│ ├── services-9.yml
│ └── services.yml
├── controllers
├── override
Expand All @@ -52,6 +61,10 @@ Let's go through each one of the above.

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

Starting from PrestaShop 9.2, module service configuration files can use several names and formats, as shown in the
example above. Only one service configuration file is loaded per folder. See [Services][sf-services] for more details
about service configuration files and their loading priority.

### `controllers/` folder

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

### `override/` folder

PHP files placed in the `override` folder will replace the ones from the Core.
PHP files placed in the `override` folder will replace the ones from the Core.

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

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

The module's main PHP file should be named the same as the module’s root folder.
The module's main PHP file should be named the same as the module’s root folder.

Example for the BlockCMS module:

Expand Down
Loading