GShop modules

Introduction


This application is based on the skeleton application using the Laminas MVC layer and module systems, on top of Rest and Rpc rhubbit core modules

Required library

Media setting

Install ffmpeg to manage video thumb auto generate process

$ sudo apt-get install ffmpeg

Install dependency

Install dependency modules with:

$ composer install

Download required npm package with

$ npm install

Generate spec

OpenApi spec / redoc

Generate your web services documentation in /public/doc/api.html from open api spec with:

$ npm run "Bundle HTML spec"


Module spec / pandoc

Download package following instruction at https://pandoc.org/installing.html

Generate your modules documentation in /public/doc/modules.html from all module *.md files with:

$ npm run "Modules spec"

Config params

This is the global application config params list.

Set this parameters in local.php|global.php under /config/autoload folder to define application behaviours:

return [
    //...
    
    'log' => true,
    'version' => 'x.y.z',
    'logpath' => '/var/www/your-app-folder/log',
    'log_level' => [
        'cli' => \Monolog\Level::Debug,
        'listener' => \Monolog\Level::Debug,
        'web' => \Monolog\Level::Debug,
    ],
    'clipath' => '/var/www/your-app-folder/vendor/bin/laminas',
    
    //...
];
Parameter Description
log If set to TRUE application log are enabled, otherwise every app log will be ignored
version Application version number express with semver syntax
logpath folder where log will be saved
log_level log criteria in use foreach of the supported area
log_level.cli command line interface log level saved in [logpath]/cli.log file
log_level.listener listener log level saved in [logpath]/listener.log file
log_level.web web log level saved in [logpath]/web.log file
clipath command line interface base path, used when application try to exec(/*cli-command*/)



ChannelManager


Description of setting to add in your application local.php / global.php file to configure ChannelManager module

List of channel

Add the channel key to your application’s configuration file and assign it aa array of configuration blocks, one for each supported channel.

return [
 // ...
 'channel' => [
     [
        'bundle' => 'my.channel.name',
        'queue_bundle' => 'test.queue',
        'content_provider' => \Your\Helper\ContentProvider::class,
        'type' => 'shopify', //or wordpress, farfetch, ...
        'api_config' => [
            // parameters required to configure channel by type
        ],
     ],
     // other channel ...
  ],
  // ...
];

Each supported channel configuration block must include the following attributes

Attribute Description
bundle unique identifying name of the channel (there cannot be two channels with the same name)
queue_bundle unique identifying queue bundle name of valid queue initialized via queue:init [options] command exposed by QueueManager module
content_provider name of subclass implementing ChannelManager\Sdk\Interface\IContentProvider interface
type one of the supported channel type, accepted values are: shopify, wordpress, farfetch … (at this moment only shopify was implemented)
api_config custom parameters needed to configure each channel type (more information in the next sections)

Channel API config

In this section you will find the different supported channel type API configuration params

Shopify

Add the following parameters to configure shopify channel

return [
 // ...
 'channel' => [
     [
        // ... rest of channel config
        'type' => 'shopify',
        'api_config' => [
            'url' => 'https://site-name.myshopify.com/admin/api/2025-04/graphql.json',
            'headers' => [
               'Accept-Language' => 'it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7',
               'X-Shopify-Access-Token' => 'your_access_token'
            ],
            'TaxonomyCategoryId' => 'gid://shopify/TaxonomyCategory/xxx-1234',
            'LocationId' => 'gid://shopify/Location/12345',
            'PublicationId' => 'gid://shopify/Publication/12345',
        ],
     ],
     // other channel ...
  ],
 // ...
];

Each shopify channel configuration block must include the following attributes

Attribute Description
url The URL of shopify GraphQL API got from shopify backend
headers key / values pair of custom headers to add on every shopify GraphQL request
- Accept-Language: required as workaround of shopify bug to set standard response message language
- X-Shopify-Access-Token: token got from shopify backend to authenticate every GraphQL request

PS: You can add any other custom headers to the two mandatory ones above.
TaxonomyCategoryId gid of a shopify category object set in every synced product mutation request
LocationId gid of a shopify location object set in every synced product mutation request
PublicationId gid of a shopify publication object set in every synced product mutation request

CLI command

List of all command line interface exposed by ChannelManager module

SYNC

 $ cm:sync [options]

Start to sync entity with id in input on channel selected by its bundle name.

Options:

  -y, --yes                        # Choose yes to confirm automatically (add -y|--yes to confirm)
  -c, --channel=CHANNEL            # The bundle name of the channel where the entity will be synced
  -t, --type=TYPE                  # The type of channel where the entity will be synced (eg. shopify|wordpress|farfetch|...
  -i, --id=ID                      # The unique application identifier of the entity to sync
  -x, --eid=EID                    # The unique external channel identifier of the entity to sync (used only if --method=delete
  -e, --entity=ENTITY              # The the entity type to sync (product|media)
  -m, --method=METHOD              # The operation to execute when sync (create|update|delete)
  -l, --log-filename=LOG-FILENAME  # Custom queue log filename
  -f, --force[=FORCE]              # Force update skipping last sync date check
  -h, --help                       # Display help for the given command. When no command is given display help for the list command
  -q, --quiet                      # Do not output any message
  -V, --version                    # Display this application version
  -n, --no-interaction             # Do not ask any interactive question
  -v|vv|vvv, --verbose             # Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Examples:

 # get application product with id 5 and create product on shopify channel with bundle name my.channel
 $ ./vendor/bin/laminas cm:sync -n -y -vvv -c my.channel -t shopify -m create -e product -i 5
  
 # get application product with id 5 and update product on shopify channel with bundle name my.channel
 $ ./vendor/bin/laminas cm:sync -n -y -vvv -c my.channel -t shopify -m update -e product -i 5
  
 # get application product with id 5 and delete product on shopify channel with bundle name my.channel
 $ ./vendor/bin/laminas cm:sync -n -y -vvv -c my.channel -t shopify -m delete -e product -i 5
  
 # useful when main media is deleted working directly with externalId
 $ ./vendor/bin/laminas cm:sync -n -y -vvv -c my.channel -t shopify -m delete -e media -x "gid://shopify/Media/1234567890" 

These commands will typically be generated by the system autonomously and executed by queuing them in a queue managed by the QueueManager module.

Database


This module implement standard Database interface based on Propel ORM

Config params

This is the module application config params list.

Set this parameters in local.php|global.php under /config/autoload folder to define application behaviours:

return [
    //...
    
    'database' => [
        'enable_log' => true,
        'logfilepath' =>  '/var/www/your-app-folder/log/database.log',
        'connections' => [
            'YourDB' => [
                'dsn' => 'mysql:host=localhost;dbname=YourDB;charset=utf8',
                'host' => 'localhost',
                'usr' => 'db-user',
                'pwd' => 'xxxxxxxxx',
                'dbname' => 'YourDB',
            ],
        ],
    ],
    
    //...
];
Parameter Description
enable_log If set to TRUE every query executed via propel ORM will be saved in [logfilepath] file
logfilepath absolute file path where log will be saved
connections list of supported db connection
connections.[dbname] connection params array of a specific db
connections.[dbname].dns dns connection param of a specific db
connections.[dbname].host host connection param of a specific db
connections.[dbname].usr username connection param of a specific db
connections.[dbname].pwd password connection param of a specific db
connections.[dbname].dbname database name of a specific db

Initialize DB

1 - Move from <project_folder> to database module folder:

$ cd module/Database

2 - run the following command to generate file in /generated-conf, /generated-sql, /generated-classes folders:

$ ../../vendor/propel/propel/bin/propel config:convert 
$ ../../vendor/propel/propel/bin/propel sql:build
$ ../../vendor/propel/propel/bin/propel model:build

3 - update autoload including /generated-classes reference with:

$ composer update

Diff / Migrate

1 - Move from <project_folder> to database module folder:

$ cd module/Database

2 - run the following commands to generate migration:

$ ../../vendor/propel/propel/bin/propel diff 
$ ../../vendor/propel/propel/bin/propel migrate
$ ../../vendor/propel/propel/bin/propel model:build

3 - update autoload including /generated-classes reference with:

$ composer update



Queue manager


A queue manager service instance will listen queue messages sent on specific queue identified by instance param ad execute cli command received in message event body.

Configure server

Install module

The queue management service could be based on a cloud hosted RabbitMQ server that require amqp module.

Install php8.2-amqp module on server

 $ sudo apt install php-amqp
 $ sudo apt install php8.2-amqp

Then add this extension to PHP with extension = 'amqp.so' in /etc/php/8.2/apache2/php.ini

Register service

To initialize this service you need to copy predefined rhubbit.queue@.service template in your systemd folder

 $ sudo cp /YourProject/module/QueueManager/bin/rhubbit.queue@.service /etc/systemd/system/rhubbit.queue@.service

and replacing your application root folder path in service ExecStart editing configuration line path before /vendor/... part

 # ...
 [Service]
 Type=simple
 ExecStart=/var/www/your_project/vendor/bin/laminas queue:manager -vvv -n -y -b %I
 # ...

then reload systemd daemon files and enable your service template

 $ sudo systemctl daemon-reload
 $ sudo systemctl enable rhubbit.queue@.service

Instantiate service

Now your can instantiate monitor and start/stop your service passing valid queue bundle name as instance param after @

 $ sudo service rhubbit.queue@<queue.bundle>.service start  # start service
 $ sudo service rhubbit.queue@<queue.bundle>.service status # monitor service status
 $ sudo service rhubbit.queue@<queue.bundle>.service stop   # stop service

Instance param <queue.bundle> must be the valid bundle name of database registered Text / RabbitMQ queue.

1 - Register queue

To register a valid queue in application database execute a command like this

 $ ./vendor/bin/laminas queue:init -y -e -b test.queue -u file:///var/www/your-project/queue/cli -t "topic1|topic2" 

Now you can use test.queue bundle name as service instance param.

2 - Check service

Now if you check the application service list with systemctl | grep rhubbit.queue@ you should see something like this

 $ systemctl | grep rhubbit.queue@
 rhubbit.queue@test.queue.service   loaded active running   Rhubbit - queue manager ON test.queue

and if you check your service status with sudo service rhubbit.queue@test.queue.service status you should see something like this

 $ sudo systemctl status scm_wp_queue.service 
 ● rhubbit.queue@test.queue.service - Rhubbit - queue manager ON test.queue
     Loaded: loaded (/etc/systemd/system/rhubbit.queue@.service; disabled; vendor preset: enabled)
     Active: active (running) since Tue 2025-07-15 17:38:40 CEST; 24min ago
   Main PID: 36527 (php)
      Tasks: 1 (limit: 2324)
     Memory: 23.6M
     CGroup: /system.slice/system-rhubbit.queue.slice/rhubbit.queue@test.queue.service
             └─36527 php /var/www/your-project/vendor/bin/laminas queue:manager -vvv -n -y -b test.queue

3 - Queue log

The queue-manager log file could be customized adding -l parameter in ExecStart command defined in /bin/rhubbit.queue@.service or system will automatically use file path set in application logpath attribute set in global.php config file

 # ...
 [Service]
 Type=simple
 ExecStart=/var/www/your-project/vendor/bin/laminas queue:manager -l /your/custom/queue.log -vvv -n -y -b %I 
 # ...

Service utils

Common command to manage systemd services:

  1. check all services systemctl
  2. check all application systemctl | grep rhubbit (every service name should start with rhubbit prefix)
  3. stop service systemctl stop YOUR-SERVICE-NAME.service or service YOUR-SERVICE-NAME stop
  4. start service systemctl start YOUR-SERVICE-NAME.service or service YOUR-SERVICE-NAME start
  5. restart service systemctl restart YOUR-SERVICE-NAME.service or service YOUR-SERVICE-NAME restart
  6. enable service systemctl enable YOUR-SERVICE-NAME.service or service YOUR-SERVICE-NAME enable
  7. disable service systemctl disable YOUR-SERVICE-NAME.service or service YOUR-SERVICE-NAME disable
  8. check service status systemctl status YOUR-SERVICE-NAME.service or service YOUR-SERVICE-NAME status
  9. check id service is enabled systemctl is-enabled YOUR-SERVICE-NAME.service
  10. reload serviced after some change systemctl daemon-reload (new service added, .service file update, …)
  11. check service crash log journalctl -u YOUR-SERVICE-NAME.service

CLI command

List of all command line interface exposed by QueueMananger module.

INIT

 $ queue:init [options]

Initialize or override queue config with input parameter.

Options:

-y, --yes                        # Choose yes to confirm automatically (add -y|--yes to confirm)
-b, --bundle=BUNDLE              # Queue bundle unique name identifier
-u, --uri=URI                    # Queue uri connection param
-t, --topics=TOPICS              # Valid list of queue topics pipeline separated (es. topic1|topic2|...
-l, --log-filename=LOG-FILENAME  # Custom queue log filename
-e, --enabled                    # Enable initialized queue
-h, --help                       # Display help for the given command. When no command is given display help for the list command
-q, --quiet                      # Do not output any message
-n, --no-interaction             # Do not ask any interactive question
-v|vv|vvv, --verbose             # Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Examples:

 $ ./vendor/bin/laminas queue:init -y -e -b test.queue -u file:///var/www/gshop-api/queue/cli -t "test1|test2" # init queue with all params
 $ ./vendor/bin/laminas queue:init -y -b test.queue # disable queue
 $ ./vendor/bin/laminas queue:init -y -e -b test.queue # enable queue


MANAGER

 $ queue:manager [options]

Initialize queue monitoring process with these criteria: - if uri and ‘topics’ are passed ignore ‘bundle’ start monitoring queue with ‘uri’ on every ‘topics’ - if bundle is passed extract queue connection params from DB start monitoring every topic of that queue defined in topics attribute, and set log name with bundle and filename with related attribute overriding log-filename input

Options:

-y, --yes                        # Choose yes to confirm automatically (add -y|--yes to confirm)
-l, --log-filename=LOG-FILENAME  # Override log filename (default value cli.log)
-b, --bundle=BUNDLE              # Queue bundle unique name used to get connection param from DB
-u, --uri=URI                    # Queue uri connection param
-t, --topics=TOPICS              # Pipeline-separated list of topics to monitor on the queue
-h, --help                       # Display help for the given command. When no command is given display help for the list command
-q, --quiet                      # Do not output any message
-V, --version                    # Display this application version
-n, --no-interaction             # Do not ask any interactive question
-v|vv|vvv, --verbose             # Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Examples:

 $ ./vendor/bin/laminas queue:manager -vvv -n -y -b test.queue -t "topic1|topic2" # listen topic1 and topic2 on test.queue getting setting from application settings
 $ ./vendor/bin/laminas queue:manager -vvv -n -y -b test.queue # listen every topic on test.queue getting setting from application settings
 $ ./vendor/bin/laminas queue:manager -vvv -n -y -u file:///queue/path/cli -t "topic1|topic2" # listen topic1 and topic2 on queue with uri file:///queue/path/cli
 $ ./vendor/bin/laminas queue:manager -vvv -n -y -u file:///queue/path/cli # listen every topic on queue with specified uri file:///queue/path/cli


PURGE

 $ queue:purge [options]

Remove all queue_event filtered via bundle, uuid, interval and status. Ignore by default messages with status success trying to remove these only if explicitly set via related attribute

Options:

  -y, --yes                        # Choose yes to confirm automatically (add -y|--yes to confirm)
  -l, --log-filename=LOG-FILENAME  # Override log filename (default value cli.log)
  -b, --bundle=BUNDLE              # Queue bundle unique name used to identify message in queue_event
  -u, --uuid=UUID                  # Queue message uuid to resync
  -s, --status=STATUS              # Pipeline separated list of valid queue message status to resync
  -i, --interval=INTERVAL          # Hours elapsed since the message was sent
  -h, --help                       # Display help for the given command. When no command is given display help for the list command
  -q, --quiet                      # Do not output any message
  -V, --version                    # Display this application version
  -n, --no-interaction             # Do not ask any interactive question
  -v|vv|vvv, --verbose             # Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Examples:

 $ ./vendor/bin/laminas queue:purge -n -y -vvv -b test.queue -s "pending|in_progress|success" # purge message from test.queue with status pending, in_progress or success 
 $ ./vendor/bin/laminas queue:purge -n -y -vvv -b test.queue -s "pending"  # purge message from test.queue with status pending
 $ ./vendor/bin/laminas queue:purge -n -y -vvv -b test.queue -u e9ab214e-9e0e-4460-b4a3-80492c457b0c  # purge message from test.queue with specified uuid
 $ ./vendor/bin/laminas queue:purge -n -y -vvv -b test.queue -i 24  # purge all message from test.queue enqueued 24 hours ago


RESYNC

 $ queue:resync [options]

Resync all queue_event filtered via bundle, uuid, interval and status retrying to enqueue all matching messages. If status filter is not set ignore only messages with status success trying to resync these only if explicitly set.

Options:

  -y, --yes                        # Choose yes to confirm automatically (add -y|--yes to confirm)
  -l, --log-filename=LOG-FILENAME  # Override log filename (default value cli.log)
  -b, --bundle=BUNDLE              # Queue bundle unique name used to identify message in queue_event
  -u, --uuid=UUID                  # Queue message uuid to resync
  -s, --status=STATUS              # Pipeline separated list of valid queue message status to resync
  -i, --interval=INTERVAL          # Hours elapsed since the message was sent
  -h, --help                       # Display help for the given command. When no command is given display help for the list command
  -q, --quiet                      # Do not output any message
  -V, --version                    # Display this application version
  -n, --no-interaction             # Do not ask any interactive question
  -v|vv|vvv, --verbose             Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Examples:

 $ ./vendor/bin/laminas queue:resync -n -y -vvv -b test.queue -s "pending|in_progress|success" # resync message from test.queue with status pending, in_progress or success
 $ ./vendor/bin/laminas queue:resync -n -y -vvv -b test.queue -s "pending" # resync message from test.queue with status pending
 $ ./vendor/bin/laminas queue:resync -n -y -vvv -b test.queue -u e9ab214e-9e0e-4460-b4a3-80492c457b0c # resync message from test.queue with specified uuid
 $ ./vendor/bin/laminas queue:resync -n -y -vvv -b test.queue -i 24 # resync all message from test.queue enqueued 24 hours ago


SEND

 $ queue:send [options]

Send message with cmd and data content on selected queue / topic: - if uri is passed ignore bundle otherwise use bundle to get queue uri from DB then combining with topic enqueue message on queue with sent params - if bundle is passed extract queue override ‘log-filename’ and log setting with data got from DB

Options:

  -y, --yes                        # Choose yes to confirm automatically (add -y|--yes to confirm)
  -l, --log-filename=LOG-FILENAME  # Override log filename (default value cli.log)
  -b, --bundle=BUNDLE              # Queue bundle unique name used to get connection param from DB
  -u, --uri=URI                    # Queue uri connection param
  -t, --topic=TOPIC                # Queue topic connection param
  -c, --cmd=CMD                    # Command to send in message on queue uri / topic
  -d, --data=DATA                  # JSON with command parameter to send in message on queue uri / topic
  -i, --info=INFO                  # Short message description
  -h, --help                       # Display help for the given command. When no command is given display help for the list command
  -q, --quiet                      # Do not output any message
  -V, --version                    # Display this application version
  -n, --no-interaction             # Do not ask any interactive question
  -v|vv|vvv, --verbose             # Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Examples:

 $ ./vendor/bin/laminas queue:send -n -y -b test.queue -t test1 -c "custom-command" -d '{"a": true, "bb": false, "xyz": 12, "n": {"test": 666}}' # send custom cli command on test.queue in topic test1 with JSON parameters 
 $ ./vendor/bin/laminas queue:send -n -y -b test.queue -t test1 -c "ls" -d '{"a": true, "l": true}' # send standard cli command (a simple ls) on test.queue in topic test1 with JSON parameters
 $ ./vendor/bin/laminas queue:send -n -y -b test.queue -t test1 -c "ls -la" -i "command short description" # send standard cli command (a simple ls -la) on test.queue in topic test1 with short description



RestIdentity


This module implement standard Database interface based on Propel ORM

Config params

This is the module application config params list.

Set this parameters in local.php|global.php under /config/autoload folder to define application behaviours:

return [
    //...
    
    'client' => [
        'base_url' => 'https://www.your-app-frontend.com',
        'confirm_url' => 'https://www.your-app-frontend.com/confirm'
    ],
    
    //...
    'security' => [
        'bundle' => 'api.project_name',
        'secret' => 'xxxxxxxxxxxx',
        'ttl' => 1800,
        'refresh_ttl' => '+5 day',
        'confirm_ttl' => '+1 hour',
        'default_role_id' => 3,
        'admin_role_id' => [1, 2],
        'enable_client_log' => true,
        'cc_user_invitation_to' => 'admin@domain.com',
    ],
    
    //...
];
Parameter Description
client main client configuration if required
client.base_url homepage url of the main client
client.confirm_url confirm account page url of the main client
security.bundle unique application bundle used to ensure JWT token by project
security.secret secret key to enforce JWT token security
security.ttl JWT token time to live in seconds
security.refresh_ttl refresh token time to live in a format accepted by strtotime()
security.confirm_ttl identity confirmation code time to live in a format accepted by strtotime()
security.default_role_id default role id assigned to new users
security.admin_role_id list of role id enabled as admin
security.enable_client_log flag used to enable DeviceLogController used to support client to log error on server with the aim to support production client error debug
security.cc_user_invitation_to add this setting optional to send a cc copy of each user invitation email to the specified address

RestMedia


Module built to implementent standard RESTFul API and controller interface to manage runtime uploaded application media

Required library

Install ffmpeg to manage video frame extracting process

$ sudo apt-get install ffmpeg

Config params

This is the module application config params list.

Set this parameters in local.php|global.php under /config/autoload folder to define application behaviours:

return [
    //...
    
    'media' => [
        'placeholder' => '/var/www/your-app-folder/public/img/img-placeholder.png',
        'cdn' => [
            'baseurl' => 'https://www.your-cdn.com',
            'basepath'=> '../your-app-cdn/public',
            'absolutepath'=> '/var/www/your-app-cdn/public',
        ],
        'entity' => [ 
            // list of entity managed by RestMedia module
        ]
    ]
    
    //...
];
Parameter Description
placeholder absolute path of placeholder file used if a media can’t be found
cdn local application CDN config params
cdn.baseurl public baseurl of the local CDN used from RestMedia module to create media public link
cdn.basepath relative base path of the local CDN used from RestMedia module to locate uploaded media folder
cdn.absolutepath absolute base path of the local CDN used from RestMedia module to locate uploaded media folder
entity list of entity with related media managed by RestMedia module stored in local CDN with path like /{entity_type}/{eid}/media/...


Entity config

Foreach entity with related media managed by RestMedia you have to define the module behavior via a config params array put under reserved key identifying the entity name, key labeled EntityType in the following example (in real project could be User, Product, MediaGallery, …)

return [
    //...
    
    'media' => [
        //...
        'entity' => [ // list of entity managed by RestMedia module
            'EntityName' => [ // entity name
                'max_file_size' => '4MB',
                'mime_types' => \RestMedia\Filter\MediaFilter::getStandardImageTypeAccepted(), // list of supported mime type for entity
                'resize' => [
                    'thumb' => [
                        'mode' => \RestMedia\Model\MediaModel::CROP_MODE,
                        'width' => 150,
                        'height' => 150,
                    ],
                    //...
                ],
                'video_screenshot_second' => 0, //video seconds to generate thumb
                'video_screenshot_extension' => "png",
                'video_screenshot' => [
                    'thumb' => [
                        'mode' => \RestMedia\Model\MediaModel::CROP_MODE,
                        'width' => 150,
                        'height' => 150,
                    ],
                    //...
                ],
            ],
            // ...
        ]
    ]
    
    //...
];
Parameter Description
max_file_size max upload file size accepted
mime_types list of mime types accepted: you can set an explicit static list or use one of the \RestMedia\Filter\MediaFilter helper methods with common mime types list
resize list of resize criteria automatically applied to every uploaded image (in the example there is only thumb criteria but you can add all match criteria you want).

Resized media will stored in folder with criteria name (in the example /{entityname}/{eid}/media/thumb
resize.[criteria] your custom resize criteria name, thumb in the example
resize.[criteria].mode resize mode, accepted values are: CROP/SCALE/RESIZE
resize.[criteria].width the resized image width
resize.[criteria].height the resized image height
video_screenshot_second param used with uploaded videos to define the instant to get screenshot
video_screenshot_extension param used with uploaded videos to define screenshot image file extension (png, jpg, …)
video_screenshot param used with uploaded videos to define screenshot image resize criteria (you can add all match criteria you want)
video_screenshot.[criteria] your custom resize criteria name, thumb in the example, this config object use the generic resize criteria (see resize.[criteria])


How to use

RestMedia is a module based on Rest and RestIdentity modules and can be used in two different ways:

  1. via /media_gallery/[:id] and /media_gallery/[:id]/media/[:mid] standard RESTFul interface
  2. extending MediaController with a custom subclass and relate application routing


RESTFul interface

Using standard RESTFul interface you will have access to classic media gallery system. In the case you have to only define MediaGallery entity config following specification in this document, then you will have access to:

  • /media_gallery/[:id] RESTFul interface to manage runtime a simple MediaGallery entity used as container of the uploaded medium
  • /media_gallery/[:id]/media/[:mid] RESTFul interface to upload and manage media uploaded inside the MediaGallery identified by :id

You can create multiple media gallery inside your application and, if you like it, related entity from different modules to it via :id unique identifier.


Extend MediaController

Extending MediaController, a subclass of AdvancedRestfulController exposed by Rest module, you can create your custom media gallery controller to fully manage business logic behind your uploading criteria.

Create your MediaGallery subclass is simple, you only need a controller like this:

    class ProductMediaController extends MediaController
    {
        private $ownerId = 0;
        private $entityId = 0;
    
        public function onDispatch(MvcEvent $e)
        {
            $this->ownerId = //set your media owner id
            $this->entityId = //set the id of main entity relate to the media
    
            return parent::onDispatch($e);
        }
    
    
        /*********************************************
         * MediaController subclass abstract methods *
         *********************************************/
    
        // return the media owner user id
        public function getOwnerId(): ?int
        {
            return $this->ownerId;
        }
    
        // return the name of the entity type related to the media
        public function getEntityType(): string
        {
            return ProductEntity::ENTITY_NAME;
        }
    
        // return the unique id of the entity type related to the media
        public function getEntityId(): int
        {
            return $this->entityId;
        }
        
        
        /*********************************************************
         * MediaController subclass optional override of methods *
         *********************************************************/
        
        // Optionally set a subclass of MediaModel as model used by
        // MediaController to implements advanced customization
        public function getMediaModel(): ?MediaModel
        {
            return new CustomMediaModel(...)
        }
    }

Then you need to init register your controller following generic Rest module criteria and defining custom related routes like this:

return [
    'router' => [
        'routes' => [
            //...
            '/api/v1/product/{id}/media' => [
                'type' => Segment::class,
                'options' => [
                    'route' => '/api/v1/product/:pid/media[/]',
                    'constraints' => [
                        'pid' => '[1-9]\d*'
                    ],
                    'defaults' => [
                        'controller' => 'RestProduct\Controller\ProductMedia'
                    ]
                ],
            ],
            '/api/v1/product/{id}/media/{id}' => [
                'type' => Segment::class,
                'options' => [
                    'route' => '/api/v1/product/:pid/media/:id[/]',
                    'constraints' => [
                        'pid' => '[1-9]\d*',
                        'id' => '[1-9]\d*'
                    ],
                    'defaults' => [
                        'controller' => 'RestProduct\Controller\ProductMedia'
                    ]
                ],
            ],
        ],
    ],
    'controllers' => [
        'invokables' => [
            'RestProduct\Controller\ProductMedia' => Controller\ProductMediaController::class,
            //...
        ],
    ],
    //...
];



Rest


This module contains standard advanced controller to support RESTFul API development based and validated on open api spec.

Required library

  1. Install module APCU Cache:

    $ sudo apt install php-apcu
    $ sudo apt install php8.2-apcu
  2. Configure APC in php.ini:

    $ vim /etc/php/8.2/apache2/php.ini

    With APC Default configuration:

    extension=apcu.so
    apc.enabled=1
    apc.shm_size=1024M
    apc.max_file_size=10M
    apc.num_files_hint=20000
    apc.user_entries_hint=20000
    apc.ttl=7200
    apc.user_ttl=7200
    apc.gc_ttl=3600
    apc.include_once_override=0
    apc.enable_cli=1

    Then reload apache

    sudo service apache2 restart
  3. Download apcu monitoring page in your web root or dedicate virtual host web root

    $ cd /your/project/root/public
    $ wget https://raw.githubusercontent.com/krakjoe/apcu/master/apc.php

    then set username and password in apc.php

  4. Reset oas_spec entry from apc cache after every open api spec update

    $ curl http://your.domain.xxx/spec-clear.php

Config params

This is the module application config params list.

Set this parameters in local.php|global.php under /config/autoload folder to define application behaviours:

return [
    //...
    
    'debug' => true,
    'oldest_supported_version' => 'x.y.z',
    'spec' => '/var/www/your-app-folder/service-spec/generated-spec/spec.yaml',
    
    //...
];
Parameter Description
debug If set to TRUE when application trigger error/exception JSON response will include full stacktrace
oldest_supported_version oldest supported version number express with semver syntax, if client request older API version via Accept-Version header will be triggered an 406 - Not acceptable response error exception
spec folder where spec yaml file will be saved

Module configuration

All the subclasses of AdvancedRestfulController, if configured, will automatically trigger standard events propagated throughout the application by Laminas and listenable by every application modules via dedicate listeners attached to onBootstrapt event of the module.

To enable your modules and related controllers to trigger and listen RestEvent you have to:

  1. extend your Module class with AbstractRestfulModule

    class Module extends AbstractRestfulModule ...
  2. implement getBundles method of the superclass returning an array key/values with:

    • key: a unique bundle name identifying your module, eg. com.rhubbit.rest.acl

    • values: a list of controller enabled to trigger this kind of events

        'com.rhubbit.rest.acl' => [
           Controller\DeviceController::class,
           Controller\IdentityController::class,
           Controller\IdentityConfirmationController::class
        ]
  3. set the event context name in the onDispatch method of the related controller (typically using the related Entity name)

     public function onDispatch(MvcEvent $e)
     {
         $this->setEventContext(ProductEntity::ENTITY_NAME);
         return parent::onDispatch($e);
     }

Event structure

Every event automatically triggered by AdvancedRestfulController contains: - bundle: unique event name composed by module bundle and context (entity name) related to the controller, eg. com.rhubbit.rest.acl/identity - event: the specific event name, eg. create.pre, update.post, … - target: reference to the object instance triggering the event - params: an array with all the parameters attached by the controller to the event

Standard events

Every AdvancedRestfulController, if added in the array returned by getBundles, will automatically trigger the event matching action managed:

HTTP Event params
GET get.pre the same input of the controller get method
GET get.post the same output that controller get method will return (the Entity)
GET get.error the same error that controller get method will return
GET getList.pre the same input of the controller getList method
GET getList.post the same output that controller getList method will return (the Entity)
GET getList.error the same error that controller getList method will return
POST create.pre the same input of the controller create method
POST create.post the same output that controller create method will return (the Entity)
POST create.error the same error that controller create method will return
PUT update.pre the same input of the controller update method
PUT update.post the same output that controller update method will return (the Entity)
PUT update.error the same error that controller update method will return
DELETE delete.pre the same input of the controller delete method
DELETE delete.post a BaseEntity object with the same input of the controller delete method (Entity at this time was deleted)
DELETE delete.error the same error that controller delete method will return


Event lister

To create a new RestEvent listener you have to:

  1. create a class and implements ListenerAggregateInterface

  2. add this class to the factories list managed by the module in the service_manager block of the controller module.config.php file php 'service_manager' => [ ... 'factories' => [ ... Listener\ProductSyncListener::class => SyncListenerFactory::class, ... ], ],

  3. implement the attach method of the ListenerAggregateInterface

    //
    public function attach(EventManagerInterface $events, $priority = - 100)
    {
        $sharedManager = $events->getSharedManager();
    
        $this->listeners[] = $sharedManager->attach('api.shopcm/product', 'create.pre', array(
            $this,
            'onProductCreating'
        ), $priority);
    
        $this->listeners[] = $sharedManager->attach('api.shopcm/product', 'create.post', array(
            $this,
            'onProductCreated'
        ), $priority);
    
       //...
    }
  4. define callback method related to every attached event, e.g. onProductCreating e onProductCreated

  5. attach this listener in the onBootstrap module event callback

    //register single event listener
    $eventManager = $app->getEventManager();
    $app->getServiceManager()
        ->get(Listener\ProductSyncListener::class)
        ->attach($eventManager);



RPC


Rpc module, built on top of Rest module, implements standard json-rpc 2.0 request: https://www.jsonrpc.org/specification.

This module inherits authentication and authorization criteria from Rest module adding not standard Api-Key and Authorization header to json-rpc protocol

Create JSON RPC Controller

Following these steps you are able to implement your custum Json RPC Controller.

  1. Create a subclass of AbstractRpcController

    class RestCalculatorController extends AbstractRpcController
    {
        // your code here ...
    }
  2. Implement your procedure handler following spec in JSON-RPC PHP Server spec here: https://github.com/matasarei/json-rpc

  3. Implement abstract method getRpcServer() of AbstractRpcController in your subclass

    class RestCalculatorController extends AbstractRpcController
    {
        public function getRpcServer(): Server
        {
            $server = new Server();
            $procedureHandler = $server->getProcedureHandler();
    
            $procedureHandler->withObject(new Calculator());
            $procedureHandler->withObject(new Test());
    
            return $server;
        }
    }
  4. Add controller to your module.config.php file:

    return [
        // ...
        'router' => [
            'routes' => [
                '/api/v1/rpc/calculator' => [
                    'type' => Segment::class,
                    'options' => [
                        'route' => '/api/v1/rpc/calculator[/]',
                        'defaults' => [
                            'controller' => 'Rpc\Controller\RestCalculator'
                        ]
                    ],
                ],
            ],
        ],
        'controllers' => [
            'invokables' => [
                'Rpc\Controller\RestCalculator' => Controller\Example\RestCalculatorController::class,
            ],
        ],
        // ...
    ]
  5. Add your rpc routes to your spec-root.yaml:

      # ...
      '/api/v1/rpc/calculator':
          $ref: '../module/Rpc/spec/Example/ExampleRpcSpec.yaml#/~1api~1v1~1rpc~1calculator'
      # ...
  6. Add standard JsonRpcErrorSchema, JsonRpcErrorSchema and JsonRpcErrorSchema to your spec-root.yaml got from module /spec folder:

      # ...
      JsonRpcRequest:
        $ref: '../module/Rpc/spec/JsonRpcRequestSchema.yaml'
      JsonRpcResponse:
        $ref: '../module/Rpc/spec/JsonRpcResponseSchema.yaml'
      JsonRpcError:
        $ref: '../module/Rpc/spec/JsonRpcErrorSchema.yaml'
      # ...
  7. Define your custom method request schema:

    • Extending standard JsonRpcRequest

          allOf:
            - $ref: '../../../../../../service-spec/spec-root.yaml#/components/schemas/JsonRpcRequest'
            - type: object
              title: add
              properties:
              method:
                type: string
                description: This method execute x + y.
                default: add
                example: add
              params:
                type: object
                description: add method params
                properties:
                  x:
                    type: integer
                    example: 20
                  y:
                    type: integer
                    example: 35
    • adding it to your spec-root.yaml

         # ...
         CalculatorRpcAddRequest:
           $ref: '../module/Rpc/spec/Example/Calculator/Method/AddRequestSchema.yaml'
         # ...
  8. Define your custom method response schema:

    • extending standard JsonRpcResponse

          allOf:
            - $ref: '../../../../../../service-spec/spec-root.yaml#/components/schemas/JsonRpcResponse'
            - type: object
              title: add
              properties:
                result:
                  type: integer
                  example: 55
    • adding it to your spec-root.yaml

         # ...
         CalculatorRpcAddResponse:
           $ref: '../module/Rpc/spec/Example/Calculator/Method/AddResponseSchema.yaml'
         # ...
  9. Finally, add you rpc route to ACL