Skip to content

PHP7 no-framework solution to build web applications

License

Notifications You must be signed in to change notification settings

vukbgit/simplex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Simplex

A tool for web developers

Table of Contents

Introduction

Simplex is a tool for developing PHP/HTML/CSS web applications. In short, it sets up a handy environment so you hopefully only have to code files specific to the project business logic: PHP (classes and some configuration file), HTML (Twig templates) and CSS or SCSS.

The goal of Simplex is to provide:

  • the structure for a PHP web application that is a compromise between:
    • simplicity
    • the latest standards and practices (as far as I know)
  • a quick and easy way to get started developing code for the application

To do so Simplex relies on:

  • Composer packages (by means of Packagist packages):
    • Simplex itself is a Composer package that:
      • takes care of installing the required libraries (for at least the minimum functionalities)
      • creates the basic starting structure for the application with some draft files almost ready to be used but that can be deleted, modified and integrated at need
    • other selected Composer packages are integrated to create the application core engine
  • NPM for Javascript and CSS packages
  • Bootstrap as CSS framework
  • Fontello for icons

NOTE ON THIS DOCUMENT: I will try to be clear and write down all the details to understand and use Simplex, for future-me, any possible colleague and anyone else interested benefit

Requirements

  • server on Linux OS (mainly tested on Debian 10/11)
  • webserver: Simplex has always used Apache 2.4+, .htaccess files are used for basic routing; I guess it could be used also Nginx also but it has ever been tested
  • PHP 8.2+ with the PHP gettext bcmath extensions enabled
  • ssh access to web space: on a shared hosting it's hard to use Composer (and Npm and Sass), you have to develop locally and commit, but I really suggest to find a provider who can give you ssh access; once I tried the power & comfort of the ssh shell I rented my own virtual machine and never turned back to shared hosting again...
  • Composer aliased (globally or locally) as composer command
  • a frontend package manager: Simplex suggests npm installed globally:
  • Sass (I currently use version 1.56.1 compiled with dart2js 2.18.4): to compile css with variables, mixings and many other useful additions and customize bootstrap

Terminology

  • root: the top folder of the Simplex installation, usually the top folder in the web accessible part of the site web space
  • application: the customized installation of Simplex for the specific project/domain
  • private: area of the application hidden from browsers
  • public: area of the application open to browsers
  • share: area of the application where third part libraries and files are store, mainly private/share for Packagist PHP libraries and public/share/node_modules for npm frontend packages
  • environment: in which the current request is handled, Simplex assumes the values of "development" and "production" but it's possible to define other variants on a per installation basis
  • area: a part of the application matching a set of routes having properties, requirements and behaviours in common, i.e "Backend", "Frontend", "Cron", every route must set an 'area' parameter and it should be formatted as a slug
  • subject: into an ERP area, a subject is the name of the system formed by:
    • a controller
    • a set of routes handled by the controller
    • the set of actions corresponding to these routes
    • the controller methods corresponding to these actions
    • the model each of this actions operate on
  • action: the specific logic associated to a route, i.e. 'list' or 'save-form', every route must set an 'action' parameter and it should be formatted as a slug

Conventions:

  • in the following explanation files and paths are written in italic
  • for each file is always given the path from the root, without leading slash

Pre-Installation - Upgrade Jobs

Before installing from scratch or upgrading from 2.x:

  • download ini config file draft

  • save it into web space outside root folder as config.ini

  • edit and set at least mandatory values following comments

  • set permanently the PATH_TO_INI_CONFIG environment variable to the absolute path to the file, adding to system user .bashrc file (usually something like home/user-name/.bashrc ) the file

      export PATH_TO_INI_CONFIG=absolute/path/to/config.ini
    
  • if logged into same console where installation/upgrade will be executed, log-out to make $PATH_TO_INI_CONFIG available to scripts

Installation

Create a composer.json in the root folder:

{
  "type": "project",
  "name": "vuk/simplex",
  "description": "Simplex app",
  "license": "MIT",
  "require": {
    "vukbgit/simplex": "^3.0"
  },
  "config": {
    "vendor-dir": "private/share/packagist",
    "bin-dir": "./"
  },
  "autoload": {
    "psr-4": {
      "Simplex\\Local\\": "private/local/simplex"
    }
  },
  "scripts": {
    "post-create-project-cmd": [
      "SlowProg\\CopyFile\\ScriptHandler::copy"
    ],
    "pre-update-cmd": [
      "Simplex\\Refactor::preRefactoring"
    ],
    "post-update-cmd": [
      "Simplex\\Refactor::postRefactoring"
    ]
  },
  "extra": {
    "copy-file": {
      "private/share/packagist/vukbgit/simplex/installation/": "."
    }
  }
}

Notes: for version constraint (^3.0) check last tag on github repository or non-develop version number on packagist.

Create the Composer project running on command line in the root folder:

    composer create-project

Simplex will:

  • install itself and the other required Composer libraries
  • copy in the root directory some files
  • make symlinks in the root directory to some shell scripts (Composer vendor binaries)
  • build the folders structure for the local application with some ready draft files

For details see Folders and Files Structure below

Upgrade

From 2.x version

  • follow Pre-Installation - Upgrade Jobs

  • add into composer.json to the "scripts" array the post update refactoring script:

    "post-update-cmd": [
      "Simplex\\Refactor::postRefactoring"
    ]
    
  • run the composer upgrade command

  • look into output for any message that requires an action

  • eventually examine the refactoring logs saved into private/local/log

  • add into composer.json to the "scripts" array the pre update refactoring script:

    "pre-update-cmd": [
      "Simplex\\Refactor::preRefactoring"
    ]
    
  • if necessary add to .gitignore private/local/simplex/log/

From a 3.x version less than 3.13

  • take note of current version, i.e. ./composer.sh show vukbgit/simplex

  • update composer packages: ./composer.sh update

  • call refactor.php for PRE stage with old version, i.e.: php8.2 private/share/packagist/vukbgit/simplex/bin/refactor.php pre 3.4.0

  • call refactor.php for POST stage, i.e.: php8.2 private/share/packagist/vukbgit/simplex/bin/refactor.php post

  • look into output and logs for any message that requires an action

  • add into composer.json to the "scripts" array the pre and post update refactoring script:

    "pre-update-cmd": [
      "Simplex\\Refactor::preRefactoring"
    ],
    "post-update-cmd": [
      "Simplex\\Refactor::postRefactoring"
    ]
    
  • if necessary add to .gitignore private/local/simplex/log/

Post-Installation Jobs

  • config.ini:
    • should be already in place (see Pre-Installation - Upgrade Jobs) with minimal configuration (otherwise nothing works...)
    • use this file to store any local information that must be as secret as possible like database credentials, passwords, secret API keys, etc.
    • simplex bash scripts all include private/share/packagist/vukbgit/simplex/bin/bootstrap.sh which needs to include config.ini so path (absolute or relative to the top level called script) must be provide as:
      • the PATH_TO_INI_CONFIG environment variable, which you can export as export PATH_TO_INI_CONFIG=path/to/config.ini:
        • temporarily into ssh session
        • permanently into home/user-name/.bashrc (or equivalent, which you shold already done before installation)
      • or passing to the called script the -i option followed by the absolute/relative path to the ini config file, i.e. simplex-script.sh -i path/to/config.ini
  • /.htaccess:
    • set default redirections, i.e /backend to /backend/sign-in-form; in any case either you're going to define a route for the plain domain request or you redirect domain request to a default route (i.e. RewriteRule ^/?$ /my-default-route [R,L]); for routes definitiosn see below
  • index.php: set $pathToIniConfig to the absolute o relative path to ini config file
  • install public packages:
    • the public\share\package.json file contains some NPM packages for:
    • edit the file if needed to include/exclude packages
    • install packages: Simplex is prearranged to use npm by means of the script private/share/packagist/vukbgit/simplex/bin/npm.sh symlinked into root folder:
      • ./npm.sh install
    • packages are installed under \public\share\node_modules
  • local configuration: file private\local\simplex\config\constants.php defines some PHP constants, most of them regard paths and it should not be necessary to modify them unless you plan to change the filesystem structure Simplex relies on; informations to be set for the local application (tech email, brand name...) are grouped into the LOCAL block at the top of the script
  • languages:
    • file private\local\simplex\config\languages.json defines used languages, contains by default definitions for English and Italian, add as many as required; language selection works this way:
      • if current route contains a 'lang' parameter (tipically a part of the route itself for multilanguage sites) that language is used
      • otherwise the first language defined into private\local\simplex\config\languages.json is used, so if you only need one language you can put its definition in first position
  • set up application template: a Twig template to be used for the whole application is provided in private/local/templates/application.twig:
    • it includes a Twig macro to display favicon generated by https://www.favicon-generator.org, if you want to use it go to website, generate icons images, upload them (i.e. to public/local/corporate/favicon) and set path as argument to the favicon() macro
  • set up routes:
    • as seen above in the .htaccess section, when browser points to your-domain.ltd there are two options:
      • an empty route is defined
      • .htacces redirects requests for plain domain to a default route
    • routes are set up into route.php files, which are stored into config folders
    • as explained into the terminology section above, Simplex logic is to divide application into logical areas, corresponding to folders; Simplex is shipped with a private/local/simplex/Backend folder as a draft for building a backend for the application, it contains a config/routes.php where 3 routes are defined for the login operation (display the login form, perform login authentication and logout)
    • Backend folder can be renamed to something else to suit application logic but some adjustments must be made (area variable value in routes.php and templates/backend.twig file name)
    • there is also a private/local/simplex/SubjectGroup folder which is a draft for the development of a subject
  • compile SASS files to CSS:
    • Simplex encourages compilation of SASS source files (.scss) to CSS files (.css).

    • private/local/simplex/config/sass.config contains a map of SASS to CSS files, each couple marked by an id (see file for explanations)

    • sass.sh is the soft link to a shell script that is used to compile css files, into root folder:

        ./sass.sh id-of-sass-file-to-be-compiled
      
    • Simplex need the compilation of at least these files:

      • application (private/local/simplex/sass/application.scss): style for the whole application (included into private/local/simplex/templates/application.twig):
        • it includes:
          • share/vukbgit/simplex/src/sass/simplex.scss the Simplex style, cannot (should not...) be customized
          • private/local/simplex/sass/variable.scss which contains sass variables for the whole application, can/should be customized and extended
        • compile with: ./sass.sh app
      • bootstrap: different application areas might need different Bootstrap components, so there is not a predefined SCSS but a couple of drafts:
        • minimal (_private/local/simplex/sass/bootstrap.scss): ./sass.sh bs
        • backend (_private/local/simplex/Backend/sass/bootstrap.scss): all the components needed by the Simplex backend templates: ./sass.sh bsb

Area set up

  • every route must be associated to an area (see "Terminology")
  • an area needs at least some configuration files and probably a template and a css file
  • Simplex provides two starting areas, Backend and Frontend, to be tweaked and used as-is or as drafts for other areas

Backend / ERP

Simplex is shipped with an ERP namespace draft and uses it to build backends and ERP applications. Here we assume the area name is "Backend" but it can be changed to anything else, you just need to rename Backend folder accordingly and substitute the "backend" word (minding letters case) accordingly. Besides the common operations discussed above, here are the steps to configure the provided Backend area :

  • .htaccess: set redirection of /backend route to /backend/sign-in-form
  • check and set up variables into private/local/simplex/Backend/config/variables.php, at least $successfulSignInRoute (defaults to backend/dashboard), the other default values should be ok out of the box
  • set up authentication methods into private/local/simplex/Backend/config/routes.php :
    • htpasswd: (enabled by default)

      • points to a htpasswd file that must be set manually, cd into desired folder (defaults to private/local/simplex/Backend/config):

          htpasswd -c .htpasswd your-username
        
      • every user defined in this file must be given a role into private/local/simplex/Backend/config/users-roles.php

    • database: (commented by default) uses a table or view, specifies fields names and conditions (like a boolean field which stores whether user is active)

  • set up logoPath into private/local/simplex/Backend/templates/sign-in-form.twig to point to a logo image to be displayted into login form
  • set up logoPath into private/local/simplex/Backend/templates/backend.twig to point to a logo image to be displayted into masthead, navbar height defaults to 48px, it can be twiked into private/local/simplex/sass/variables.scss
  • customize Area style (private/local/simplex/Backend/sass/backend.scss) that just include ERP style and related variables (private/local/simplex/Backend/sass/variables.scss)
  • compile the SASS files:
    • sign-in: ./sass.sh si
    • backedn: ./sass.sh be
  • set up the subject called by the $successfulSignInRoute route (see "Subject set-up below"):
  • to integrate jodit uploader:
    • into root folder run ./composer.sh create-project --no-dev jodit/connector public/share/jodit

    • add to any rich text input the (built by a displayRichTextEditor macro) configuration to display upload related Jodit buttons (i.e. image) and uploader path:

              ...
              options: {
                buttons: 'bold, italic, underline, strikethrough | ul, ol | cut, copy, paste | indent, outdent, left, right, center, justify | link | image | undo, redo | source,fullsize, preview | about',
                uploader: {
                  url: '/public/share/jodit/index.php?action=fileUpload',
                }
              }
              ...
      
    • into public/share/jodit/Application.php remove the exception from checkAuthentication method and add eventual authentication logic

    • copy public/share/jodit/default.config.php as public/share/jodit/config.php

    • create a folder for uploaded images into public path, i.e. public/local/simplex/richTextUploads

    • if using a cvs system

      • include jodit in commit, edit public/share/jodit/.gitignore.php:

              composer.lock
              .idea/
        
              tests/_data/
              tests/_output/
              tests/_support/
              tests/acceptance/
              tests/files/
        
      • exclude folder from commits, i.e. for Git:

        • create a public/local/simplex/richTextUploads/.gitignore file

        • paste into file:

              # Ignore everything in this directory
              *
              !.gitignore
          
    • edit public/share/jodit/config.php and:

      • set up sources folders, i.e.

              $basePath = '/public/local/simplex/';
              $richTextUploadFolder = $basePath . 'Foo/richTextUploads';
        
              $config = [
                  'sources' => [
                    'default' => [
                      'root' => $_SERVER['DOCUMENT_ROOT'] . $richTextUploadFolder,
                      'baseurl' => ((isset($_SERVER['HTTPS']) and $_SERVER['HTTPS']) ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . $richTextUploadFolder . '/',
                      'extensions' => [
                        'jpg',
                        'png',
                      ]
                    ],
                  ],
              ]
        
        • edit other options such as access control

This is the Backend folder structure:

  • private/local/simplex
    • Backend
      • config
        • constants.php:
          • AREA_NAVIGATION_PATH: path to file with navigation menu definition, dfefaults to private/local/simplex/Backend/config/navigation.php
        • di-container.php: DI container definitions, shipped with just the authentication controller definition, it should not be necessary to modifiy or add definitions here, unless you have a class to be used across the whole area
        • navigation.php: navigation menus definition, return an array with one ore more menus definitions to be shared among area's pages, see file for format details
        • premissions-roles: map between permissions keys and area users roles (as defined into the authentication system), only necessary if you want to restrict access to some actions to some roles
        • route.php: definitions for area routes not related to some specific subject (i.e. sign in form)m shipped with default sign in form, sign in and sign out routes; contains some hard coded paths to authentication realted files that should not be necessary to change
        • users-roles: map between users names and roles, it is decoupled from authentication system (and user retrival data) to be as flexible as possible
        • variables: area level variables (such as slugged area name and default authentication object to be used in area routes), included into subjects di-container.php and routes.php specific file
      • sass:
        • bootstrap.scss: Bootstrap 4 components to be included in Bootstrap area style sheet, preset for minimal standard functionalities (menu, form, tables...), customize and recompile at need
      • templates: sign-in form and area shared template, used mainly to store translations and logo path, to be customized
    • SubjectGroup: subjects namespace can be organized into levels, useful when there are more the ten subjects at play
      • Subject: sdubjec level files, every type of file regarding the subject should be stored into its folder
        • config:
          • crudl.php: definitions for CRUDL functionalities, such as input filters to be used to grab fields values from save form or whether to use field to filter displayed table records
          • di-container.php: DI container definitions, shipped with just the controller and model definitions, it should be enough for standard subjects
          • model-php: model configuration (table. view. pruimary key)
          • navigation.php: definition for navigation associated to subject actions, shipped with default CRUDL actions (list, insert, update and delete), it should be enough for standard subjects
          • routes.php: definition for routes associated to subject actions, shipped with one definition that covers default CRUDL actions (list, insert, update and delete), and any action identified by one slugged action key and an optional primary key value, it should be enough for standard subjects
          • variables: subject level variables (such as subject namespace and slugged subject name )
        • templates: subject related templates, shape up subject UI and provide translations
          • crudl-form.twig: HTML for subject insert, update and delete forms by using macros defined into private/share/packagist/vukbgit/simplex/src/templates/form/macros.twig or by writing HTML code directly
          • list.twig: template for subject records list, defaults to table, it's necessary to set up table headers and cells
          • subject.twig: contains subject labels
        • Controller.php: base subject controller class, extends Simplex\Erp\ControllerAbstract, correct namespace must be set but already inherits all of the methods needed by CRUDL operations, to be extended for additional actions (with protected methods named after the PSR1 form of the slugged action name)
        • Model.php: base subject model class, extends Simplex\Model\ModelAbstract, correct namespace must be set but already inherits all of the methods needed by CRUDL operations, to be extended for additional actions (with protected methods named after the PSR1 form of the slugged action name)
    • bin: shell scripts, it can be useful to soft link them into web root
      • composer.sh: to use composer with a PHP versione different from the system default one
    • config: application configuration files
      • constants.php: application brand label and tech email (to be showed into exceltions) must be set, other values are meinly paths that should not be necessary to change
      • db.php: database account settings (takes data from config.ini)
      • languages.json: languages available to application, first one is used as default, shipped with Italian (default for most of my applications) and English, customize at need
      • sass.config: used by the web root sass.sh bash script to speed up SASS files compilation, shipped with some ready to use common paths, see file for details on format
    • sql: this folder might contain useful text files with SQL commands and snippets
      • views.sql: databases views definition, I usually keep them here since I find it more handy for editing
    • sass: application level SASS files
      • application.scss: rules to be applied to the whole applications
      • bootstrap-variables.scss: to override Bootstrap variables, included into bootstrap.scss BEFORE the Bootstrap own variables file
      • bootstrap.css: this file can be used to compile an application level bootstrap file if there is no need to have area level ones
      • functions.scss: SASS functions
      • variables.scss: application specific SASS variables i.e. colors or some specific ERP settings (navbars dimensions)
    • templates:
      • application.twig: ready to use top level application template
  • public
    • local: folder for application files accessible by browsers, will be filled at least by compiled CSS files and probabily by much more application assets
    • share
      • package.json: default NPM packages
      • npm.sh: symlinked into web root to handle npm commands
    • .htaccess: Apache configuration file, grants access to real files (CSS, Javascript, imagess, etc) and redirects every other request to index.php

Subject set up

The following steps show how to set up an ERP subject, that is a subject which implies a database model and the related CRUD UI, as the major part of a backend's subjects should be; it is also possible to use into a backend other kind of subjects (a dasboard for example), in this case see the frontend explanation below

  • set up subject database architecture: besides a main table, in order to deal with internazionalization and file upload, Simplex relies over two accessory table, here is the complete overview of the possible subject architecture:
    • the main table holds language-indipendent data, at least the primary key; it should be named after the model key, usually snake case even if it can be any name as long as it is set into config/crudl.php

    • internazionalization: Simplex aims to be localization ready, so there should be a table for any language-dependent information, named main-table-name_locales (with the mandatory _locales suffix), with the following structure (naming convention is to be sctrictly followed for Simplex to handle correctly data saving):

      • a primary key (integer autoincrement) named this-table-name_id (with the mandatory _id suffix)
      • a language_code (char 2) field, which hold the languages codes used as key into config/languages.json (the ISO-639-1 codes)
      • a foreign key field targeted to the main table primary key with a CASCADE delete setting
      • any custom field (text or varchar) for localized pieces of information as needed by subject
    • in case subject needs files upload, define a table named main-table-name_uploads (with the mandatory _uploads suffix)

      • this is the table structure:
        • a primary key (integer autoincrement) named this-table-name_id
        • a foreign key field targeted to the main table primary key with a CASCADE delete setting
        • a field named upload_key (varchar 64 should be enough) to store the record type of upload (the "field" used as key into the "uploads" array defined into /config/model.php)
        • a field named file_name (varchar 256) to store the record type of upload (the "field" used as key into the "uploads" array defined into /config/model.php)
      • uploads information need to be manually added to the view (see below)
    • the view:

      • a basic view should be created and set up into config/model.php
      • in case of uploads the view should be joined over the uplods table:
        • for each upload field defined into config/model.php add a left join to the uploads table on the primary key and the upload_key field (with the value of the field key)
        • extract file_name field values with the name of the upload key; if field allows multiple uploads, group values, order them by primary key (so that order can by set by dragging files in the GUI) and join tem with a pipe: ,GROUP_CONCAT(DISTINCT join-table.file_name ORDER BY join-table.primary-key SEPARATOR '|') AS upload-key
        • group by primary key in case of upload fields that allow multiple uploads in the UI
      • in case of a localized subject a localized view named main-table-name_locales is mandatory: it must join main table over the locales one and expose primary keys fields, language_code field (besides localized informations of course)
      • example:
        • table "foo" defined as

            CREATE TABLE `foo` (
                `foo_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
                `non_localized_string` varchar(48) NOT NULL,
                `date` date,
                PRIMARY KEY (`foo_id`)
            )
          
        • uploads definiton into subject config/model.php:

            ...
            'uploads' => [
                'pdf_file' => [
                    'raw' => (object) []
                ],
                'single_image' => [
                    'thumb' => (object) [
                        'handler' => ['\Simplex\Local\SUBJECT\NAMESPACE\Controller','resizeImage'],
                        'parameters' => [100,100]
                    ],
                    'full' => (object) [
                        'handler' => ['\Simplex\Local\SUBJECT\NAMESPACE\Controller','resizeImage'],
                        'parameters' => [800,600]
                    ]
                ],
                'multiple_image' => [
                    'thumb' => (object) [
                        'handler' => ['\Simplex\Local\SUBJECT\NAMESPACE\Controller','resizeImage'],
                        'parameters' => [100,100]
                    ],
                    'full' => (object) [
                        'handler' => ['\Simplex\Local\SUBJECT\NAMESPACE\Controller','resizeImage'],
                        'parameters' => [800,600]
                    ]
                ]
            ],
            ...
          
        • the uploads table:

            CREATE TABLE `foo_uploads` (
            `foo_uploads_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
            `foo_id` int(10) unsigned NOT NULL,
            `upload_key` varchar(64) NOT NULL,
            `file_name` varchar(256) NOT NULL,
            PRIMARY KEY (`foo_uploads_id`),
            KEY `foo_id` (`foo_id`),
            CONSTRAINT `foo_uploads_ibfk_1` FOREIGN KEY (`foo_id`) REFERENCES `foo` (`foo_id`) ON DELETE CASCADE
            ) 
          
        • basic view:

            CREATE VIEW v_foo AS SELECT
            f.*
            ,fusi.file_name AS single_image
            ,GROUP_CONCAT(DISTINCT fumi.file_name ORDER BY fumi.foo_uploads_id SEPARATOR '|') AS multiple_image
            FROM foo AS f
            LEFT JOIN foo_uploads AS fusi
            ON f.foo_id = fusi.foo_id
            AND fusi.upload_key = 'single_image'
            LEFT JOIN foo_uploads AS fumi
            ON f.foo_id = fumi.foo_id
            AND fumi.upload_key = 'multiple_image'
            GROUP BY f.foo_id
          
        • the internationalization table:

            CREATE TABLE `foo_locales` (
            `foo_locales_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
            `language_code` char(2) NOT NULL,
            `foo_id` int(10) unsigned NOT NULL,
            `title` varchar(256) NOT NULL,
            `content` text NOT NULL,
            PRIMARY KEY (`foo_locales_id`),
            KEY `foo_id` (`foo_id`),
            CONSTRAINT `foo_locales_ibfk_1` FOREIGN KEY (`foo_id`) REFERENCES `foo` (`foo_id`) ON DELETE CASCADE
            )
          
        • the internationalization view:

            CREATE VIEW v_foo_locales AS SELECT
            f.*
            ,fl.language_code
            ,fl.title
            ,fl.content
            FROM v_foo AS f
            LEFT JOIN foo_locales AS fl
            ON f.foo_id = fl.foo_id
          
    • Simplex is shipped with a convenient private/local/simplex/docs/views.sql text file where views definition can be written; often database manager SQL editor are not handy, storing views definition into a plain SQL/text file, editing through an editor with synthax highlighting and copy and paste into the db application can be a solution and provides also a backup

  • each subject files are contained into a folder named after the subject
  • ponder the position of the subject into application architecture:
    • it can be used application wide, so its folder should reside into private/local/simplex
    • it can be used only inside ont specific area, so it should reside into private/local/simplex/area-name
    • it can share some business logic with other subjects, so it should reside into private/local/simplex/[area-name]/subjects-group-name; in case application uses more than a few subjects I usually try to group them
  • use private/local/simplex/SubjectGroup/Subject folders as a draft, copy them and customize files; or even better use an already configured subject folder and edit just the files formatted below in bold italic. Working into subject folder:
    • config/variables.php: set up subject namespace and slug form, namespace must reflect subject folder position (i.e. Simplex\Local\Backend\Subject-Group-Name\Subject-Name)
    • config/model.php: set up subject model definition
    • rename config/routes.draft.php to config/routes.php, default configure dynamic route should cover at least the basic CRUD operations (list, inser form, insert operation, update form, update operation, delete form, delete operation)
    • rename config/di-container.draft.php to config/di-container.php
    • edit Controller.php and Model.php and correct namespace to the same value used into config/routes.variables.php
    • edit config/crudl.php and set up:
      • table localization (localized property, boolean), whether model table uses an accessory table for localization (see "set up database architecture" above)
      • table fields filters to be used to retrieve fields values passed by save form
    • config/navigation.php contains rules to display the UI navigation for basic CRUD actions, it can be customized to remove some of them or add more actions, for permissions logic see subject permission below
  • set up subject template labels in templates/labels.twig, at least subject label is required but fields labels are used int list and save form templates
  • edit templates/list.twig to set up fields displayed into records table, there is a tableheader block for headers and a records loop for table cells with fields values
  • edit templates/crudl-form.twig to set up fields displayed into insert/update and delete forms; any valid html can be inserted into modelInputs block but a bunch of useful Twig macros are defined into /private/share/packagist/vukbgit/simplex/src/templates/form/macros.twig which is included by default; use macros whose name end by 'group' di build a Bootsrap field form complete with label
  • set up subject permission for roles into private/local/simplex/Backend/config/permissions-roles.php:; by default into private/local/simplex/Backend/config/navigation.php permission manage-SUBJECT-KEY is required for the user to use global (list and insert) and record actions (update and delete), so permission manage-SUBJECT-KEY must be added and user's role must be included into permission's allowed roles array
  • include subject into area navigation into private/local/simplex/Backend/config/navigation.php
  • set up subject navigation label in private/local/simplex/Backend/templates/backend.twig, into the areaNavigationLabels hash
  • perform sign-out/sign-in to reload permissions

Debugging

  • PHP layer: Simplex uses Whoops for printing exceptions in development environment and Kint for dumping variables; in particular Kint is available as two functions:
    • 'x($var)': dumps a variable to HTML aoutput
    • 'xx($var)': dumps a variable and exits the scripts immediately
  • Twig templates: the dump function is available as a wrapper for Kint dump function, i.e. '{{ dump(my-variable-of-any-type) }}'

Internazionalization

  • PHP/Twig templates:
    • Simplex uses gettext and is shipped with a set of English and Italian translations for the messages contained into the provided UI

    • translations can be inserted into:

      • PHP files using the gettext() function or the _() alias (see PHP gettext documentation)
      • Twig templates using the trans block and filter from the (included by default) i18n extension (see documentation)
    • private/share/packagist/vukbgit/simplex/bin/translations.php script can be used to extract translations

    • folder where translations files are stored is specified by the TRANSLATIONS_DIR constant and defaults to private/local/simplex/locales

    • this is the translations workflow:

      • after installation, into TRANSLATIONS_DIR English and Italian translations for Simplex core messages are stored

      • translations extraction from relies on Twig templates cache; if your local templates use any user defined Twig filter or function templates cache building will be broken, so you need to:

        • put the Twig filters/functions definitions into a public method of some of your local classes
        • create one or more files under private/local/simplex named templates-helpers.php: if this file exists it sill be automatically included by the translation script (there is a draft file private/local/simplex/bin/templates-helpers.draft.php)
        • into the templates-helpers.php file call all of the template helpers builder methods
      • when local translations are added run script to update local messages, .po and .mo files are saved into TRANSLATIONS_DIR for every language defined into private/local/simplex/config/languages.json:

          php private/share/packagist/vukbgit/simplex/bin/translations.php update local
        
      • download .po files, translate with Poedit or other similar software

      • upload resulting .po and .mo files back to TRANSLATIONS_DIR

    • in case site uses a PHP version different from system one if must be specified the complete path to PHP binary, i.e.

        /opt/php-7.3.5/bin/php private/share/packagist/vukbgit/simplex/bin/translations.php update local
      
    • at installation time it is also created in the root folder the script translations.sh that can be used to shortcut translation process, call it from root folder this way:

            ./translations.sh update local
      
  • database: Simplex encourages a localization ready database architecture, even when site uses just one language (see Subject set up > database architecture)

Emails

Simplex uses mjml integrated into Twig templates by means of mjml-php to build responsive emails. A starting templates is provided into private/local/simplex/templates/emails/base.twig

Icon Fonts

Simplex uses Fontello for icons and it breaks icons into logical groups, each with its Fontello folder, so far:

  • public/share/simplex/form/Fontello for form related icons (100 reserved unicode values, from 0100 to 0164)
  • public/share/simplex/Erp/Fontello for ERP (Backend) related icons (100 reserved unicode values, from 0165 to 01C9)
  • public/share/brands/Fontello for brands related icons, i.e. social media or file types (20 reserved unicode values, from 01CA to 01DE)

Unicode codes are assigned so that, if icons are used into css (setting CSS properties content to unicode code and font-family to "fontello") icons do not overlap, if other application specific icons are added they should take unicode codes from 02BC (included) onward. Note: when using unicode codes into CSS remember to check text-transform (lowercase or uppercase) because each icon corresponds to a unicode charachter with a specific case and, if element inherits case from context, it could display the wrong gliph.

Development to Production

If you keep separate development and production environment and manage pubblication through a git repository you can use some bash scripts soft linked into web root at installation time:

  • development environment:
    • git-setup-dev.sh: interactive script that asks for repository URL, git user email and git user name, sets up the repository, makes first significative commit and pushes it to repository
    • git-push-all.sh: push all changes made since last commit, it adds all content of root folder and subfolders, so any folder/file to be excluded from commits must be added to .gitignore; if you need to push only some changes you must add, commit and push manually
  • production environment:
    • copy composer.json file from development environment
    • run composer create-project
    • run git-setup-prod.sh, interactive script that asks for repository URL and sets up repository for pulling
    • run update-all.sh which:
      • cleans DI container and templates cache, beware of tmp folder path, (see Post-Installation Jobs > bash scripts settings
      • updates Composer packages
      • updates NPM packages through npm

Simplex Logic overview

Logical Structure

  • Simplex\ControllerAbstract: basic controller for a route that does not display a page
    • has DI container and response object as injected dipendencies
    • route MUST pass 'action' (in slug form) and 'area' parameters
    • stores request and its parameters parameters
    • sets language
    • executes the method named after the PSR1 translation of the action parameter slug
  • Simplex\ControllerWithTemplateAbstract: controller for a route that displays a page
    • extends Simplex\ControllerAbstract so inherits its properties and functionalities plus the following
    • has template engine and cookies manager as injected dipendencies
    • builds some template helpers
    • passes to template some constants and variables as parameters
  • Simplex\Erp\ControllerAbstract: controller for a route into an ERP area of the application
    • extends Simplex\ControllerWithTemplateAbstract so inherits its properties and functionalities plus the following
    • identifies the current subject (see "Terminology" above):
      • all the files relative to a subject MUST be in the same folder, inside the _Simplex\Local _ namespace
      • the subject name is the name of the folder files are into (which is the name of the last part of the subject's files namespace) turned from PSR1 format to slug (with every upper case letter prepended by hypen and turned to lower case, i.e. 'NewsCategories' > 'news-categories')
      • stores the subject under $this->subject
    • instances and injects the mandatory model:
      • there MUST be a class defined into subject namespace that:
        • is named 'Model'
        • must extend Simplex\Model\ModelAbstract
        • must have git a configuration file under subject-namespace\config\model.php
      • stores the model under $this->model
    • loads CRUDL config which is mandatory for ERP and contains informations for the CRUDL interface to be exposed (such as the input filters to be used with each model field) and must be saved into a subject-namespace\config\crudl.php
    • gets users options for the subject from cookies (options are set into cookie from the UI) and stores them into $this->userOptions
    • loads navigations:
      • load area navigations (side bar menu) from the file area-namespace\config\navigation.php
      • load subject navigations (globa actions tabs, table record action links) from the file subject-namespace\config\navigation.php
      • each route contained into navigations voices is tested against current URL to check whether it's the currently selected one
    • builds some ERP specific template helpers
    • passes to template some ERP specific constants and variables as parameters

Application Flow

  • .htaccess
    • sets a PHP environment variable base on the domain to decide the current environment
    • intercepts every request and redirects to index.php
  • index.php:
    • requires Composer autoload
    • searches for files named constants.php under folder private/local/simplex, during installation private/local/simplex/config/constants.php is created (see file for details)
    • set up the Error Handler based on the environment
    • instances a Dipendency Injector Container loading definitions from private/share/packagist/vukbgit/simplex/config/di-container.php (see file for details)
    • the DI Container instances the Dispatcher (which is another name for a request handler)
    • the dispatcher loads the middleware queue from the MIDDLEWARE_QUEUE_PATH constant value (defaults to private/share/packagist/vukbgit/simplex/config/middleware.php), Simplex default queue is composed by:
      • the Router which loads routes definitions from any file named "routes.php" stored under the private/local/simplex folder (even in subdirectories); the route definition must contain an "action" parameter (private/local/simplex/config/route.php contains more details about routes definitions)
      • the Simplex Authentication middleware that:
        • fires conditionally if an "authentication" parameter is found inside the current route definition
        • if fired checks whether the user is currently authenticated, otherwise redirects to a configured url
      • the Request Handler (no, not the Dispatcher from above, there is a bit of naming confusion on this matter...), which is responsible for processing the current route, invokes the Route Handler (a local class) specified into the route definition which must inherit from one of the Simplex\Controller abstract classes
      • the Route Handler:
        • stores all of the request parameters and the response object into class properties
        • calls a method named after the "action" route parameter
        • this method performs all the tasks needed by the action and usually renders a template injecting HTML code into the response
      • the Dispatcher returns the response to the index.php scope
    • the HTTP status code of the response is checked and if different from 200 (which means "everything went fine") gets the appropriate HTML code from a private/share/packagist/vukbgit/simplex/src/errors/ file and injects it into the response
    • the Emitter is instantiated and returns the response to the browser

Folders and Files Structure

Simplex extends the classes namespace logic to every file in the application;: the local namespace starts from the folder defined into private/local/simplex/config/constants.php LOCAL_DIR constant (defaults to private/local/simplex) and is named by default Simplex\Local.

Into this folder the classes are arranged as the typical application, by business domain logic (i.e. the News folder for all classes related to news, the Customer folder, etc). But also every other file with different purpose (configuration files, html templates, SASS files...) should follow this logic; so there is no grouping by function first (a top config folder, a top views folder, etc.), but instead by namespace/business logic first (so /News/config and News/templates folders).

This is because typically application development proceeds by domain logic: adding the News functionality means adding at least a News class, some News configuration (routes and DI container definitions) and some News views (HTML templates for backend and frontend); if all of these files are scattered through local folder subfolders I find it harder to develope, mantain and "clone" functionalities to be used as draft for new ones

So here are folders and files as installed from Simplex, from the installation root folder:

  • private: all files that CANNOT be directly accessed by browser
    • local: files developed for the application
      • simplex: top level namespace folder for application files, every class defined inside has base namespace Simplex\Local
        • Backend: backend namespace draft folder
        • Frontend: frontend namespace draft folder
        • bin: created at installation time for useful bash scripts
          • composer.sh: allows to use composer with a PHP version different from the system default one used by the PHP CLI application, useful on a system with multiple PHP versions installed; it's a good idea to soft link it into root
        • config: configuration files for whole application to be customized
          • constants.php: environment constants, quite self explanatory, some of them should be set right after installation; NOTE: most of the regards paths Simplex uses for inclusions, it shouldn't be necessary to change them; if so beware that a pair of paths are hard coded into index.php prior to including this file and should be changed manually
          • db.php: database configuration, returns a PHP object, gets configuration from config.ini
          • di-container.php: definition to be used by the DI Container to instantiate the classes used by the application; it integrates private/local/share/vukbgit/simplex/src/config/di-container.php/ which stores the definitions for classes used by the Simplex engine
          • languages.json: languages used by the application, indexed by a custom key (the one proposed is the ISO-639-1 two letters code); if the route passes a "language" parameter, language is searched for otherwise first one defined (defaults to English) it's used
          • sass.config: custom format file to speed up Sass files compilation using the sass.sh script: you can define for each file to be compiled a custom id (any string) and source and destination paths, so you you ca use the shell and call from the root folder sass file-id to compile the minified CSS version
        • sass: some scss empty drafts to help compile Bootstrap and some application css
          • application.scss: rules for the whole application
          • bootstrap-variables.scss: it is included BEFORE the file with the variables.scss shipped with Bootstrap to override Bootstrap built-in variables
          • bootstrap.scss: main file to compile Bootstrap css, includes only the most commonly used components, uncomment lines to include other functionalities; private/local/simplex/config/sass.config already contains configuration to compile this file by means of the root sass.sh file, just executing in the shell './sass.sh bs'
          • functions.scss: definitions for some useful Sass functions
          • variables.scss: Sass variables to be used by the application
        • templates: some ready to use and customize Twig templates
    • share: files installed through Composer and possibly other third-part libraries from other sources
      • packagist: libraries installed by means of Packagist
        • vukbgit
          • simplex: shared Simplex modules used by application, some explanations about the less obvious ones:
            • bin: bash scripts, some of the soft linked into root at installation composer project creation time
            • installation: folders and files copied at installation time ready to be used and/or to be customized
            • src: classes and other files used by Simplex at runtime
              • config: configuration files
                • di-container.php: definition to be used by the DI Container to instantiate the classes used by the Simplex engine; it is integrated by ANY file with the same name found under the private/local/simplex folder
                • middleware.php: middleware queue to be processed by the Dispatcher, can be overridden setting MIDDLEWARE_QUEUE_PATH value into private\local\simplex\config\constants.php
              • errors: HTML files to be displayed in case of HTTP errors raised by the request
              • templates: ready to use Twig templates for backend areas with CRUDL functionalities
        • all the other Composer libraries used by the application
  • public: all files that CAN be accessed by browser
    • local: files developed for the application such as compiled css files and javascript files
    • share: libraries installed from npm and any other third-part frontend javascript and css asset
      • package.json: npm configuration file, requires Bootstrap and jQuery latest plus other useful libraries, customize at need
    • .htaccess: redirects ALL requests beginning with "public/" to index.php except the ones for files really existing into filesystem (css, js, etc.)
  • .gitignore: in a development/production flow I commit file to a private repository form the development site and pull them into the production one; this .gitignore file excludes some Simplex folders/files from commit
  • .htaccess: root Apache directives
    • redirects ALL requests for the root directory to public/index.php
  • composer.json:
    • sets vendor directory to private/share/packagist
    • sets bin directory to ./ so that symlinks are created into root for some shell scripts
    • sets autoload application directory to private/local/simplex mapping this path to Simplex\Local namespace
    • requires the Simplex package (which takes care of requiring the other needed packages)
  • index.php: application bootstrap file, since it is stored into site root all PHP includes in every file work with absolute path form site root, see "Application Flow" above for details
  • sass.sh: soft link to the helper script private/share/packagist/vukbgit/simplex/bin/sass.sh to compile Sass files, see the private/local/simplex/config/sass.config explanation above for details
  • npm.sh: soft link to the helper script public/share/npm.sh to manage npm packages into public/share folder, call it ./npm.sh npm-command, i.e ./npm.sh install foolibrary to perform the installation into local/share/node_modules/foolibrary

Migration from v2 to v3

  • index.php: Zend -> Laminas
  • FILTER_SANITIZE_STRING -> FILTER_SANITIZE_SPECIAL_CHARS:
  • grep -rl FILTER_SANITIZE_STRING private/local/simplex | xargs sed -i 's/FILTER_SANITIZE_STRING/FILTER_SANITIZE_SPECIAL_CHARS/'
  • ERP:
    • list:{% block rows %} {% for record in records %} {{ tableMacros.displayRowBegin(_context, loop, record) }} {{ record.FIELD }} {{ tableMacros.displayRowEnd(_context, loop, record) }} {% endfor %}{% endblock %}
    • crudl-form:
      • dateTimePickers bound for a date range now unified
    • css:
      • ERP style no more directly included but integrated into area (i.e. Backend) scss file:
        • create area scc file (copy from installation folder), i.e. private/local/simplex/Backend/sass/backend.css
        • compile it, i.e. ./sass.sh be
        • include into area template, i.e. private/local/simplex/Backend/templates/backend.twig
  • FORMS:
    • alla captchas dependencies and functionalities removed, implement honey trap technique
    • update (copy from installation folder) calendar dates format for languages into private/local/simplex/config/languages.json
  • TWIG:
    • move if conditions out of for loops, regular expression to search for them: grep -rE "{%\s+for\s+[a-zA-Z ,]+\s+in\s+[a-zA-Z\.]+\s+if" private/local
  • SASS:
    • enclose arithmetic operations into calc() function
  • BOOTSTRAP 5:
    • .ml-* and .mr-* -> .ms-* and .me-*.
    • .sr-only -> .visually-hidden
    • form grid: form-row -> row
    • .form-inline: use grid row > col / col-md-auto
    • all af data-* attributes in collapse (i.e. menu voices), carousels, etc become data-bs-*
    • text-alignment: -left -> -start; right->end
    • remove input-group-append / prepend
    • close buttons (modal, alert...):
      • class close -> btn-close
      • data-dismiss -> data-bs-dismiss
      • remove inner span
  • SPREADSHEET READER/WRITER: reader moved to phpoffice/phpspreadsheet should be fine, writer implementation currently still broken!

Considerations

  • I choose not to use any framework because I want to be 100% in control of the flow inside the application
  • Simplex uses third party classes for almost every specialized task (DI container, routing, dispatching, emitting...)
  • I coded some components into Simplex only when I couldn't find an external library to accomplish some task the way I needed: for example I wrote the nikic/fastroute middleware to be able to pass custom route parameters
  • design choices: I tried to search documentation, mostly seeking "no framework" suggestions (see references below), and taking a look to existing frameworks (although I am no expert in this field because I started structuring my code for re-use since 2000); I want Simplex to be up-to-date but also to be, well, simple and there is no agreement on every topic, for example the use of a DI Container. Therefore I made my (very questionable) choices, keeping always in mind the I needed a tool to build web applications in the fastest and most flexible way
  • So I ended up with a framework myself?! Honestly I do not know

API Documentation

API dcoumentation generated with phpDocumentor can be found at [https://vukbgit.github.io/simplex]

References