Skip to main content
Version: X-Cart 5.6.0

Module System

X-Cart core can be extended with modules that introduce new and customize existing functionality.

Each module has an Author ID (AuthorId) and Module ID (ModulesId). AuthorId and ModuleId are letter-first alpha-numeric sequences and use the UpperCamelCase notation.

Directory Structure​

The module code and resources are located in the <XCart>/modules/{AuthorId}/{ModuleId} folder.

The module directory structure generally adheres to the Symfony Bundle Structure.

Additional folders:

  • docs - changelog;
  • public - regular location for resources storage with a specific folder structure inside;
  • templates - regular location for templates storage with a specific folder structure inside.

Public and Templates​

Templates and resources are divided into interfaces and zones. Generally, there are three interfaces (web, mail, pdf) and two zones (admin and customer). The common zone is a parent for all other zones. The mail and pdf zones override the web zone.

Manifest​

The <XCart>/modules/{AuthorId}/{ModuleId}/config/main.yaml file is required for all modules:

version: 5.5.0.0
type: common # the module type, "common" is the mostly used type, it can also be "skin", "payment", or "shipping".
authorName: 'X-Cart team' # a readable author-name
moduleName: 'Sample Module' # a readable module name
description: 'The sample module' # module's brief description
minorRequiredCoreVersion: 0 # the module minor core version (the 3rd number)
dependsOn: { } # a list of module IDs (in the '{AuthorId}-{ModuleId}' format) with dependencies on this module
incompatibleWith: { } # a list of module IDs (in the '{AuthorId}-{ModuleId}' format) incompatible with this module
showSettingsForm: false # the module settings page availability

Modules can also use the following images as interface icons. <XCart>/modules/{AuthorId}/{ModuleId}/config/images/icon.png <XCart>/modules/{AuthorId}/{ModuleId}/config/images/iconAlt.png

Module-Core Interaction​

General ways of the module's interaction with the core are as follows:

  • Code injection through code generation
  • Symfony DI (decoration, event-dispatcher, etc.)
  • ViewLists

Code Injection through Code Generation​

The executed code is generated base on the core code from the <XCart>/classes folder and the modules code from <XCart>/modules/{AuthorId}/{ModuleId}/src.

Suppose the core contains a <XCart>/classes/XLite/Example.php file with the following class defined:

<?php

namespace XLite;

class Example
{
public function someMethod()
{
return 'SomeMethodResult';
}
}

To change the someMethod method behavior, create an undefined module class and specify a specific annotation there (but better copy the original class FQCN changing XLite to {AuthorId}/{ModuleId}).

For example, we have the XCExample\Module1 module with the <XCart>/modules/XCExample/Module1/src/Example.php file.

<?php

namespace XCExample\Module1;

use XCart\Extender\Mapping\Extender;

/**
* @Extender\Mixin
*/
class Example extends \XLite\Example
{
public function someMethod()
{
return parent::someMethod() . ' someAdditionalResult';
}
}

As a result of the code generation, three classes will be created:

<?php

namespace XLite;

class Example extends \XCExample\Module1\Example
{
}
<?php

namespace XCExample\Module1;

use XCart\Extender\Mapping\Extender;

/**
* @Extender\Mixin
*/
class Example extends \XLite\ExampleAncestor
{
public function someMethod()
{
return parent::someMethod() . ' someAdditionalResult';
}
}
<?php

namespace XLite;

class ExampleAncestor
{
public function someMethod()
{
return 'SomeMethodResult';
}
}

This way, the XCExample\Module1\Example class overrides the original implementation, but the rest of the code will still use the original class name.

Symfony DI​

To interact with Symfony DI, you must declare a module as a Symfony Bundle. For this purpose, create the {ModuleId}Bundle class and store it in the <XCart>/modules/{AuthorId}/{ModuleId}/src/{ModuleId}Bundle.php file.

For example, we have the XCExample\Module1 module with the <XCart>/modules/XCExample/Module1/src/ModuleBundle.php file.

<?php

namespace XCExample\Module1;

use Symfony\Component\HttpKernel\Bundle\Bundle;

final class Module1Bundle extends Bundle
{
}

As a result, the module will become an efficient Symfony bundle. You can upload the <XCart>/modules/XCExample/Module1/config/service.yaml file to the core to extend basic Symfony facilities after X-Cart configuration. That is not typical for Symfony and is used to allow modules to adjust X-Cart configuration.

ViewLists​

Besides adding templates and widgets to lists (see Rendering), you can also use the module to adjust the templates and widgets lists. For this purpose, create a module as a Symfony Bundle and then create one of the following event handlers:

  • xcart.service.view-list.collect-mutation.before
  • xcart.service.view-list.collect-mutation
  • xcart.service.view-list.collect-mutation.after
  • xcart.service.view-list.apply-mutation.before

The \XCart\Event\Service\ViewListMutationEvent object used to adjust the lists arrives in the event handler.

public function addMutations(array $mutations): void
{
foreach ($mutations as $subject => $mutation) {
$this->addMutation($subject, $mutation);
}
}

/**
* The mutation can be in the following formats:
* 1. To remove from a single list:
* [
* {remove_definition}
* ]
* 2. To remove from the single list and insert to the single list (move)
* [
* {remove_definition},
* {insert_definition}
* ]
* 3. To remove from several lists or/and insert to several lists
* [
* 'to_remove' => [{remove_definition}, ...],
* 'to_insert' => [{insert_definition}, ...]
* ]
* {remove_definition}: 'list_name' | ['list_name'{, 'interface'}]
* {insert_definition}: 'list_name' | ['list_name'{, (int) weight{, 'interface'}}]
*
* @param string $subject FQCN or Template relative path
* @param array $mutation Mutation definition
*/
public function addMutation(string $subject, array $mutation): void

For example**:**

Handler registration (<XCart>/modules/XCExample/Module1/config/services.yaml)

services:
XCExample\Module1\Core\EventListener:
tags:
- { name: kernel.event_listener, event: xcart.service.view-list.collect-mutation, method: onCollectViewListMutations }

Handler:

namespace XCExample\Module1\Core;

use XLite;
use XCart\Event\Service\ViewListMutationEvent;

final class EventListener
{
public function onCollectViewListMutations(ViewListMutationEvent $event): void
{
$event->addMutations([
'layout/content/main.location.twig' => [
ViewListMutationEvent::TO_REMOVE => [
['layout.main', XLite::INTERFACE_WEB, XLite::ZONE_CUSTOMER ],
],
ViewListMutationEvent::TO_INSERT => [
['center.top', 1000, XLite::INTERFACE_WEB, XLite::ZONE_CUSTOMER ],
],
]
])
}
}

As a result, the layout/content/main.location.twig template will be deleted from the layout.main list and added to the center.top list with the 1000sorting weight.

To check what modules are enabled, use the XCart\Domain\ModuleManagerDomain service.

services:
XCExample\Module1\Core\EventListener:
arguments:
$moduleManagerDomain: '@XCart\Domain\ModuleManagerDomain'
tags:
- { name: kernel.event_listener, event: xcart.service.view-list.collect-mutation, method: onCollectViewListMutations }
namespace XCExample\Module1\Core;

use XCart\Domain\ModuleManagerDomain;
use XCart\Event\Service\ViewListMutationEvent;
use XLite;

final class EventListener
{
private ModuleManagerDomain $moduleManagerDomain;

public function __construct(
ModuleManagerDomain $moduleManagerDomain
) {
$this->moduleManagerDomain = $moduleManagerDomain;
}

if ($this->moduleManagerDomain->isEnabled('CDev-GoSocial')) {
$event->addMutations([
'modules/CDev/GoSocial/product/details/parts/common.share.twig' => [
ViewListMutationEvent::TO_REMOVE => [
['product.details.page.info', XLite::INTERFACE_WEB, XLite::ZONE_CUSTOMER ],
],
ViewListMutationEvent::TO_INSERT => [
['product.details.page.image', 20, XLite::INTERFACE_WEB, XLite::ZONE_CUSTOMER ],
],
],
]);
}
}

As a result, the changes will apply only if the CDev-GoSocial module is enabled.