Project 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*/) |
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
Notification
This module implement Notification lifecycle sending:
- email message with
phpmailer/phpmailerover SMTP protocol - push notification with
kreait/firebase-phpover firebase google service
You can get phpmailer and firebase-php
with composer.
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 [
//...
'notification' => [
'push' => [
'secret' => APPLICATION_ROOT . '/secret/firebase.json',
],
'email' => [
'smtp_options' => [
'host' => 'smtps.aruba.it',
'port' => 465,
'username' => 'info@storeoverview.it',
'password' => 'ftvKC9nhnFPtw^rC',
'encryption' => PHPMailer::ENCRYPTION_SMTPS,
'from_address' => 'info@storeoverview.it',
'from_name' => 'SOV Dev',
'extra' => [ //extra email params
'key_1' => "value 1",
'key_2' => "value 2"
]
],
],
],
//...
];
| Parameter | Description |
|---|---|
push.secret |
firebase configuration json |
email.smtp_options.host |
SMTP server url |
email.smtp_options.port |
SMTP server port |
email.smtp_options.username |
SMTP account username |
email.smtp_options.password |
SMTP account username |
email.smtp_options.encryption |
SMTP connection encryption |
email.smtp_options.from_address |
SMTP sender address used in sent email |
email.smtp_options.from_name |
SMTP sender name used in sent email |
email.smtp_options.extra |
array of key / value couple passed to every message template to enable advanced message customization |
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:
- via
/media_gallery/[:id]and/media_gallery/[:id]/media/[:mid]standard RESTFul interface - extending
MediaControllerwith 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 simpleMediaGalleryentity 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
Install module APCU Cache:
$ sudo apt install php-apcu $ sudo apt install php8.2-apcuConfigure APC in
php.ini:$ vim /etc/php/8.2/apache2/php.iniWith 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=1Then reload apache
sudo service apache2 restartDownload 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.phpthen set username and password in
apc.phpReset
oas_specentry 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:
extend your
Moduleclass withAbstractRestfulModuleclass Module extends AbstractRestfulModule ...implement
getBundlesmethod of the superclass returning an array key/values with:key: a unique bundle name identifying your module, eg.
com.rhubbit.rest.aclvalues: a list of controller enabled to trigger this kind of events
'com.rhubbit.rest.acl' => [ Controller\DeviceController::class, Controller\IdentityController::class, Controller\IdentityConfirmationController::class ]
set the event context name in the
onDispatchmethod 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:
create a class and implements
ListenerAggregateInterfaceadd this class to the factories list managed by the module in the
service_managerblock of the controllermodule.config.phpfilephp 'service_manager' => [ ... 'factories' => [ ... Listener\ProductSyncListener::class => SyncListenerFactory::class, ... ], ],implement the
attachmethod of theListenerAggregateInterface// 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); //... }define callback method related to every attached event, e.g.
onProductCreatingeonProductCreated…attach this listener in the
onBootstrapmodule event callback//register single event listener $eventManager = $app->getEventManager(); $app->getServiceManager() ->get(Listener\ProductSyncListener::class) ->attach($eventManager);