From 608e8877219826c6b2545a7ddd657012a7401a09 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 21 Feb 2020 17:51:16 +0100 Subject: [PATCH 01/22] Restructure --- SUMMARY.md | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/SUMMARY.md b/SUMMARY.md index ca1105b..6727d03 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -17,31 +17,42 @@ * [Packages]({%}?page=example_module/packages) ## API -* [Routing]({%}?page=basics/routing) -* [Dispatching]({%}?page=basics/dispatching) -* [Views]({%}?page=basics/views) +### Message +* [Uri]({%}?page=services/uri) * [Requests]({%}?page=basics/requests) * [Responses]({%}?page=basics/responses) -* [Cache]({%}?page=datastorage/cache) -* [Session]({%}?page=datastorage/session) -* [Cookie]({%}?page=datastorage/cookie) +* [Mail]({%}?page=services/mail) + +### DataStorage * [LocalStorage]({%}?page=datastorage/localstorage) * [Database Connection]({%}?page=datastorage/database/connection) * [DataMapper]({%}?page=datastorage/database/datamapper) * [Queries]({%}?page=datastorage/database/queries) -* [Styles and Layout]({%}?page=frontend/styles_and_layout) -* [Charting]({%}?page=services/charting) -* [Codes]({%}?page=services/codes) -* [Events]({%}?page=services/events) +* [Cache]({%}?page=datastorage/cache) +* [Session]({%}?page=datastorage/session) +* [Cookie]({%}?page=datastorage/cookie) + +### System * [Filesystem]({%}?page=services/filesystem) +* [Events]({%}?page=services/events) * [Logging]({%}?page=services/logging) * [Tasks]({%}?page=services/tasks) -* [Mail]({%}?page=services/mail) -* [Encoding]({%}?page=services/encoding) -* [Encryption]({%}?page=services/encryption) + +### StdLib * [Localization]({%}?page=services/localization) * [Money]({%}?page=services/money) * [Queues]({%}?page=services/queues) * [Collection]({%}?page=services/collection) + +### UI +* [Styles and Layout]({%}?page=frontend/styles_and_layout) +* [Charting]({%}?page=services/charting) +* [Codes]({%}?page=services/codes) +* [Routing]({%}?page=basics/routing) +* [Dispatching]({%}?page=basics/dispatching) +* [Views]({%}?page=basics/views) * [Validation]({%}?page=services/validation) -* [Uri]({%}?page=services/uri) \ No newline at end of file + +### Security +* [Encoding]({%}?page=services/encoding) +* [Encryption]({%}?page=services/encryption) \ No newline at end of file From e5e997145d6e1ff9c5fd5cd7d6534a97cf9bf1a7 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Mon, 24 Feb 2020 21:21:53 +0100 Subject: [PATCH 02/22] move ApplicationAbstract --- example_app/app.md | 4 ++-- example_module/module.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/example_app/app.md b/example_app/app.md index ae29b40..39f866a 100644 --- a/example_app/app.md +++ b/example_app/app.md @@ -41,7 +41,7 @@ echo $App->run(); // outputs the application response namespace app; -use phpOMS\ApplicationAbstract; /* provides many member variables which are often shared with controllers */ +use phpOMS\Application\ApplicationAbstract; /* provides many member variables which are often shared with controllers */ use phpOMS\Dispatcher\Dispatcher; use phpOMS\Message\Http\HttpRequest; use phpOMS\Message\Http\HttpResponse; @@ -203,7 +203,7 @@ return [ namespace app\controller; -use phpOMS\ApplicationAbstract; +use phpOMS\Application\ApplicationAbstract; use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; use phpOMS\Views\View; diff --git a/example_module/module.md b/example_module/module.md index 555f38a..9d135a5 100644 --- a/example_module/module.md +++ b/example_module/module.md @@ -139,12 +139,12 @@ namespace Modules\Navigation\Admin; use phpOMS\DataStorage\Database\DatabaseType; use phpOMS\DataStorage\Database\DatabasePool; -use phpOMS\Module\InfoManager; +use phpOMS\Module\ModuleInfo; use phpOMS\Module\InstallerAbstract; class Installer extends InstallerAbstract { - public static function install(string $path, Pool $dbPool, InfoManager $info) + public static function install(string $path, Pool $dbPool, ModuleInfo $info) { parent::install($path, $dbPool, $info); From 611b1f0d8e50151bd5f371d88b1ac15ab509f498 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Mon, 24 Feb 2020 23:02:57 +0100 Subject: [PATCH 03/22] make installer final --- example_module/module.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example_module/module.md b/example_module/module.md index 9d135a5..98bd54d 100644 --- a/example_module/module.md +++ b/example_module/module.md @@ -142,7 +142,7 @@ use phpOMS\DataStorage\Database\DatabasePool; use phpOMS\Module\ModuleInfo; use phpOMS\Module\InstallerAbstract; -class Installer extends InstallerAbstract +final class Installer extends InstallerAbstract { public static function install(string $path, Pool $dbPool, ModuleInfo $info) { From f524c3aea3b0270e19bd7abfc0cf9929cb6a231a Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Tue, 25 Feb 2020 22:46:54 +0100 Subject: [PATCH 04/22] update phpstan version --- quality/inspections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quality/inspections.md b/quality/inspections.md index c07b05d..2170c44 100644 --- a/quality/inspections.md +++ b/quality/inspections.md @@ -39,7 +39,7 @@ Every module needs to have a `Admin` directory containing a class called `AdminT With phpstan the code base is statically analyzed based on its configuration. This will help you to follow some of the "best" practices we enforce. ```sh -php vendor/bin/phpstan analyse --autoload-file=phpOMS/Autoloader.php -l 7 -c Build/Config/phpstan.neon --error-format=prettyJson ./ > Build/test/phpstan.json +php vendor/bin/phpstan analyse --autoload-file=phpOMS/Autoloader.php -l 8 -c Build/Config/phpstan.neon --error-format=prettyJson ./ > Build/test/phpstan.json ``` ## Jasmine From e2a56b02293b6792e5071034ff7c6c3c9fdc7d7a Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Tue, 3 Mar 2020 18:52:31 +0100 Subject: [PATCH 05/22] add image compression --- .github/workflows/image.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/image.yml diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml new file mode 100644 index 0000000..578ad75 --- /dev/null +++ b/.github/workflows/image.yml @@ -0,0 +1,18 @@ +name: Compress images +on: [push] + paths: + - '**.jpg' + - '**.png' + - '**.webp' +jobs: + build: + name: calibreapp/image-actions + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@master + + - name: Compress Images + uses: calibreapp/image-actions@master + with: + githubToken: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 415ba053c92543acfd85f12fd920c33febf28308 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Tue, 3 Mar 2020 18:58:28 +0100 Subject: [PATCH 06/22] fix action --- .github/workflows/image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml index 578ad75..cff9c35 100644 --- a/.github/workflows/image.yml +++ b/.github/workflows/image.yml @@ -1,5 +1,5 @@ name: Compress images -on: [push] +on: push paths: - '**.jpg' - '**.png' From 1e90a7d449dc09e7d55bec8ff7eada71d8a4d08d Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Tue, 3 Mar 2020 19:05:04 +0100 Subject: [PATCH 07/22] fix action --- .github/workflows/image.yml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml index cff9c35..d2fb2d4 100644 --- a/.github/workflows/image.yml +++ b/.github/workflows/image.yml @@ -1,9 +1,15 @@ name: Compress images -on: push - paths: - - '**.jpg' - - '**.png' - - '**.webp' +on: + push: + paths: + - '**.jpg' + - '**.png' + - '**.webp' + pull_request: + paths: + - '**.jpg' + - '**.png' + - '**.webp' jobs: build: name: calibreapp/image-actions From 9680610c020226ca92bef24472e806bd9311532c Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Thu, 5 Mar 2020 22:14:32 +0100 Subject: [PATCH 08/22] fixes Orange-Management/Orange-Management#46 --- basics/routing.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/basics/routing.md b/basics/routing.md index a417c23..2897e25 100644 --- a/basics/routing.md +++ b/basics/routing.md @@ -162,4 +162,8 @@ $router->route( 'APP_NAME', ORG_ID, ACCOUNT ); -``` \ No newline at end of file +``` + +## Notes + +* 2-character routes on the first level are discouraged because they may conflict with ISO 639-1 codes (e.g. /hr/staff/list) \ No newline at end of file From 450371d0eb154548c94569f16896fb8d743996f1 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Thu, 12 Mar 2020 18:02:43 +0100 Subject: [PATCH 09/22] remove db prefix --- datastorage/database/connection.md | 3 --- datastorage/database/queries.md | 31 +++++++++++------------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/datastorage/database/connection.md b/datastorage/database/connection.md index d399d56..9f31e0b 100644 --- a/datastorage/database/connection.md +++ b/datastorage/database/connection.md @@ -14,7 +14,6 @@ $con = new MysqlConnection([ 'login' => 'root', /* db login name */ 'password' => 'root', /* db login password */ 'database' => 'oms', /* db name */ - 'prefix' => 'oms_', /* db table prefix */ ]); ``` @@ -32,7 +31,6 @@ $con = ConnectionFactory::create([ 'login' => 'root', 'password' => 'root', 'database' => 'oms', - 'prefix' => 'oms_', ]); ``` @@ -62,7 +60,6 @@ $dbPool->create('read', 'login' => 'root', 'password' => 'root', 'database' => 'oms', - 'prefix' => 'oms_', ] ); ``` \ No newline at end of file diff --git a/datastorage/database/queries.md b/datastorage/database/queries.md index be37841..5bf007e 100644 --- a/datastorage/database/queries.md +++ b/datastorage/database/queries.md @@ -14,34 +14,25 @@ The database query builder provides a uniform way to write default database quer The query builder is used for regular CRUD operations on the database. -#### Prefix - -Projects often use a prefix for all of the tables. For this project the default prefix is `oms_`. - -```php -$query = new Builder(); -$query->prefix('oms_'); -``` - #### Select, Insert, Update, Delete Both `select` and `insert` expect the column names as parameter. The `where`, `from` and `into` clause can be necessary depending on the type of operation like a normal sql query. ```php -$query->prefix(...)->select('columnA', 'columnB')->from('table')->where(...); -$query->prefix(...)->insert('columnA', 'columnB')->values('a', 'b')->into('table'); +$query->select('columnA', 'columnB')->from('table')->where(...); +$query->insert('columnA', 'columnB')->values('a', 'b')->into('table'); ``` The `update` expects the table name which should be updated and then the `set` function to define the columns and new values. ```php -$query->prefix(...)->update('table')->set(['columnA' => 'a'])->set(['columnB' => 'b'])->where(...); +$query->update('table')->set(['columnA' => 'a'])->set(['columnB' => 'b'])->where(...); ``` The `delete` function only expects the `from` and `where` clause to identify the to delete columns in a table. ```php -$query->prefix(...)->delete()->from('table')->where(...); +$query->delete()->from('table')->where(...); ``` ##### Random @@ -51,8 +42,8 @@ $query->prefix(...)->delete()->from('table')->where(...); The `from` part of a query accepts `string`, `array`, `\Closure`, `From`, `Builder` as parameter. ```php -$query->prefix(...)->select(...)->from('table'); -$query->prefix(...)->select(...)->from('tableA', 'tableB'); +$query->select(...)->from('table'); +$query->select(...)->from('tableA', 'tableB'); ``` #### Into @@ -64,7 +55,7 @@ The `into` part of a query accepts `string`, `array`, `\Closure`, `Into`, `Build The basic `where` clause expects a column, operator, value and boolean concatenater which is used to concatenate multiple where clauses. ```php -$query->prefix(...)->select(...)->from(...)->where('columnA', '=', 123)->where('columnB', '=', 'abc', 'or'); +$query->select(...)->from(...)->where('columnA', '=', 123)->where('columnB', '=', 'abc', 'or'); ``` For easier use additional `where` clauses are defined such as: @@ -80,7 +71,7 @@ For easier use additional `where` clauses are defined such as: The `limit` expects an integer. ```php -$query->prefix(...)->select(...)->from(...)->where(...)->limit(3); +$query->select(...)->from(...)->where(...)->limit(3); ``` #### Offset @@ -88,7 +79,7 @@ $query->prefix(...)->select(...)->from(...)->where(...)->limit(3); The `offset` expects an integer. ```php -$query->prefix(...)->select(...)->from(...)->where(...)->offset(3); +$query->select(...)->from(...)->where(...)->offset(3); ``` #### Order @@ -96,7 +87,7 @@ $query->prefix(...)->select(...)->from(...)->where(...)->offset(3); The ordering is performed by `orderBy`. ```php -$query->prefix(...)->select(...)->from(...)->where(...)->orderBy('columnA', 'DESC'); +$query->select(...)->from(...)->where(...)->orderBy('columnA', 'DESC'); ``` The `newest` and `oldest` operation are a small wrapper which automatically order by `DESC` and `ASC` respectively. @@ -106,7 +97,7 @@ The `newest` and `oldest` operation are a small wrapper which automatically orde Grouping of columns can be achieved through `groupBy`. ```php -$query->prefix(...)->select(...)->from(...)->where(...)->groupBy('columnA', 'columnB'); +$query->select(...)->from(...)->where(...)->groupBy('columnA', 'columnB'); ``` #### Join From a62465c53bc20a0b97c62c29598e2542dcc8bc43 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Sun, 29 Mar 2020 12:02:49 +0200 Subject: [PATCH 10/22] expand naming conventions --- standards/general.md | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/standards/general.md b/standards/general.md index 6906cda..60c9a1d 100644 --- a/standards/general.md +++ b/standards/general.md @@ -132,7 +132,9 @@ if (isTrue == true Switch statements must have a `default` case. -## Constants +## Naming + +### Constants Constants must be written with capital letters and snake case. @@ -140,18 +142,38 @@ Constants must be written with capital letters and snake case. CONSTANT_TEST = true; ``` -## Variables +### Function -Variables must be written in camel case. +Functions must be written in camelCase. -### Boolean variables +### Variables + +Variables must be written in camelCase. + +#### Boolean variables Boolean variable names should start with a boolean expression (e.g. is, has) -#### Boolean return value +##### Boolean return value Functions which return a boolean value should start with expressions such as is*, has* or similar expressions to indicate that the function returns a boolean value. +### Database + +#### Tables + +Tables must be written in snake_case + +#### Fields / Columns + +Fields/columns must be written in snake_case and must be preceded by the table name. + +```sql +table_name_field_name +``` + +This allows every field/column name to be unique. + ## Quotation All string representations should use single quotes `''` unless `""` provides significant benefits in a specific use case. @@ -162,7 +184,7 @@ All string representations should use single quotes `''` unless `""` provides si ## Todos -Most issues should be documented in the code as todos and vice versa. +Most issues should be documented in the code as todo and vice versa. ```php /** @@ -171,5 +193,7 @@ Most issues should be documented in the code as todos and vice versa. * This way developers can see todos directly in the code without going to an external source. * Todos must not have empty lines in their descriptions. * If the external ressources have empty lines they must be removed in the todo comment. + * 1. list item 1 + * 2. list item 2 */ ``` \ No newline at end of file From b956e6f1b7408ff83d405814d8211665900469f7 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Thu, 30 Apr 2020 21:37:31 +0200 Subject: [PATCH 11/22] add multiline ternary --- standards/general.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/standards/general.md b/standards/general.md index 60c9a1d..75221e5 100644 --- a/standards/general.md +++ b/standards/general.md @@ -119,6 +119,15 @@ if (...) { $result = condition ? expr1 : expr2; ``` +Multiline ternary expressions should be indented. + + +```php +$result = condition + ? very long expr1 which should be written like this + : very long expr2 which should be written like this; +``` + Multiline `if` conditions should begin with a logical opperator `or`/`and`. ```php From 61b93762f6a1103327fa11666a2d683609aa01c8 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 22 May 2020 17:31:23 +0200 Subject: [PATCH 12/22] add issue information --- standards/general.md | 54 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/standards/general.md b/standards/general.md index 75221e5..e53c33a 100644 --- a/standards/general.md +++ b/standards/general.md @@ -197,7 +197,7 @@ Most issues should be documented in the code as todo and vice versa. ```php /** - * @todo Orange-Management/Repository#IssueNumber + * @todo Orange-Management/Repository#IssueNumber [issue:information] * Below comes the issue/todo description. * This way developers can see todos directly in the code without going to an external source. * Todos must not have empty lines in their descriptions. @@ -205,4 +205,54 @@ Most issues should be documented in the code as todo and vice versa. * 1. list item 1 * 2. list item 2 */ -``` \ No newline at end of file +``` + +The issue information can be used to provide additional information such as priority, difficulty and type. + +### Priority + +Structure: + +```php +[p:{PRIORITY}] +``` + +Possible priorities are: + +* high +* low +* medium + +### Difficulty + +Structure: + +```php +[d:{DIFFICULTY}] +``` + +Possible difficulties are: + +* first +* beginner +* expert +* medium + +Difficulties marked with first are perfect for people who would like to contribute to the project for the first time. + +### Type + +Structure: + +```php +[t:{TYPE}] +``` + +Possible types are: + +* feature +* optimization +* performance +* question +* security +* todo (= default) \ No newline at end of file From 6f860814c8e315920ec587e31adbe3105c943410 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Mon, 8 Jun 2020 23:29:14 +0200 Subject: [PATCH 13/22] add some more datamapper explanations --- datastorage/database/datamapper.md | 84 ++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 10 deletions(-) diff --git a/datastorage/database/datamapper.md b/datastorage/database/datamapper.md index f18359c..7b5d4f2 100644 --- a/datastorage/database/datamapper.md +++ b/datastorage/database/datamapper.md @@ -2,7 +2,7 @@ ## Models -Models can be constructed in what ever way you like, all of the mapping logic is defined in the data mapper itself. It is however recommended to provide the following member variables if applicable (names can be different): +Models can be constructed in what ever way you like, all of the mapping logic is defined in the data mapper itself. However, it is recommended to provide the following member variables if applicable (names can be different): ```php private $id = 0; @@ -32,6 +32,21 @@ In a similar fashion as the `$createdAt` variable often it is also necessary to One model can only be stored in one table. With the `$table` variable it's possible to specify the table name. This variable is compulsory. It's important to note that by extending a model you also need to implement a data mapper that can access multiple tables. In that case it's also necessary to extend the data mapper of the extended module. +### Model + +The `$model` variable can be optionally used in order to specify the model this mapper is supposed to populate. By default the mapper will try to find a model in the same directory as the mapper without the `Mapper` suffix in its name. + +Default behavior example: + +* Mapper name: `\test\path\TestMapper` +* Default maodel: `\test\path\Test` + +If the model is defined somewhere else or has a different name, the `$model` variable name should be used to define the correct model. E.g. + +```php +protected static string $model = OtherModel::class; +``` + ### Columns In the `$columns` array all columns, respective model variables and data types need to be specified. @@ -45,6 +60,17 @@ protected static array $columns = [ The `name` contains the field name in the database, the `type` represents the data type and `internal` is the string representation of the model variable name. +#### Searchable columns + +In order to make columns searchable you have to add `'autocomplete' => true` as column information to the respective column. + +```php +protected static array $columns = [ + 'db_field_name_1' => ['name' => 'db_field_name_1', 'type' => 'int', 'internal' => 'model_var_name_1', 'autocomplete' => true], + 'db_field_name_2' => ['name' => 'db_field_name_2', 'type' => 'string', 'internal' => 'model_var_name_2'], +]; +``` + #### Types Possible types are: @@ -53,9 +79,9 @@ Possible types are: * string * bool * float -* \DateTime -* serializable (will call `serialize()`) -* json (will call `jsonSerialize()`) +* DateTime +* Serializable (will call `serialize()`) +* Json (will call `jsonSerialize()`) ### Has many @@ -66,7 +92,7 @@ protected static array $hasMany = [ 'model_var_name_3' => [ 'mapper' => HasManyMapper::class, 'table' => 'relation_table_name', - 'dst' => 'relation_destinaiton_name', + 'dst' => 'relation_destination_name', 'src' => 'relation_source_name', ], ]; @@ -74,6 +100,8 @@ protected static array $hasMany = [ The `mapper` contains the class name of the mapper responsible for the many models that belong to this model. The `table` contains the name of the table where the relations are defined (this can be the same table as the source model or a relation table). If a model is only in relation with one other model this relation can be defined in the same table as the model and this `table` field can be `null`. The `dst` field contains the name of field where the primary key of the destination is defined. The `src` field is only required for models which have a relation table. This field contains the name of the field where the primary key of the source model is defined. +#### Relation is defined in the model table + A many to one or one to one relation would look like the following: ```php @@ -81,12 +109,14 @@ protected static array $hasMany = [ 'model_var_name_3' => [ 'mapper' => HasManyMapper::class, 'table' => null, - 'dst' => 'relation_destinaiton_name', + 'dst' => 'relation_destination_name', 'src' => null, ], ]; ``` +#### Relation is defined in a relation table + A many to many relation which can only be defined in a relation table looks like the following: ```php @@ -94,15 +124,33 @@ protected static array $hasMany = [ 'model_var_name_3' => [ 'mapper' => HasManyMapper::class, 'table' => 'relation_table_name', - 'dst' => 'relation_destinaiton_name', + 'dst' => 'relation_destination_name', 'src' => 'relation_source_name', ], ]; ``` +#### Single field relations + +By defining a `column` it's also possible to only populate the model with a single column/field value from another table or model. + +```php +protected static array $hasMany = [ + 'my_title' => [ + 'mapper' => L11nTagMapper::class, + 'table' => 'tag_l11n', + 'external' => 'tag_l11n_tag', + 'column' => 'title', + 'self' => null, + ], +]; +``` + +In the example above the model member variable `my_title` will be populated with the value `title` from the `L11nTagMapper`. Here the `L11nTagMapper` will to a reverse lookup of the column name for the variable `title` and return the content. + ### Owns one -It's possible to also define a relation in the source module itself. This can be acomplished by using the `$ownsOne` variable. In this case the model itself has to specify a field where the primary key of the source model is defined. +It's possible to also define a relation in the source module itself. This can be accomplished by using the `$ownsOne` variable. In this case the model itself has to specify a field where the primary key of the source model is defined. ```php protected static array $ownsOne = [ @@ -113,11 +161,11 @@ protected static array $ownsOne = [ ]; ``` -The `mapper` field contains the class name of the mapper of the source model. The `src` field contains the database field name where the primary key is stored that is used for the realation. +The `mapper` field contains the class name of the mapper of the source model. The `src` field contains the database field name where the primary key is stored that is used for the relation. ### Belongs to -The reverse of a has one is a belongs to. This allows to also load models that a specific model belongs to. This can be acomplished by using the `$belongsTo` variable. In this case the model itself has to specify a field where the primary key of the source model is defined. +The reverse of a has one is a belongs to. This allows to also load models that a specific model belongs to. This can be accomplished by using the `$belongsTo` variable. In this case the model itself has to specify a field where the primary key of the source model is defined. ```php protected static array $belongsTo = [ @@ -129,3 +177,19 @@ protected static array $belongsTo = [ ``` The `mapper` field contains the class name of the mapper of the destination model. The `dest` field contains the database field name where the primary key is stored that this model belongs to. + +## Conditionals + +Conditionals provide a general way to filter the desired result of models. You can think about conditionals as SQL `WHERE` clauses. A conditional uses the model member variable name instead of the database table name. + +```php +WithConditionalMapper::withConditional('language', 'en')::getAll(); +``` + +This query returns all models of the `WithConditionalMapper` which have a member variable with the value `en`. In the background the mapper does a reverse lookup, checks the column name which is associated with the `language` member variable and filters the database result accordingly. + +By default conditionals are recursive and get applied to all models which are somehow referenced in the request (e.g. has many models which have also a `language` member variable). In some cases this is undesired and the user wants to specify the models where the conditionals should be applied to. This can be achieved in the following way: + +```php +WithConditionalMapper::withConditional('language', 'en', [FirstModelToApplyTo::class, SecondModelToApplyTo::class, ...])::getAll(); +``` \ No newline at end of file From 2feeee7e7e29fac6d4dcbcb5270522addc74747f Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Sun, 5 Jul 2020 22:02:08 +0200 Subject: [PATCH 14/22] add conditional requirement for datamapper --- datastorage/database/datamapper.md | 1 + 1 file changed, 1 insertion(+) diff --git a/datastorage/database/datamapper.md b/datastorage/database/datamapper.md index 7b5d4f2..8080acf 100644 --- a/datastorage/database/datamapper.md +++ b/datastorage/database/datamapper.md @@ -141,6 +141,7 @@ protected static array $hasMany = [ 'table' => 'tag_l11n', 'external' => 'tag_l11n_tag', 'column' => 'title', + 'conditional' => true, 'self' => null, ], ]; From 6d8e52d31d8601351fb260aa5c012e0dcb1919f0 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Sat, 11 Jul 2020 21:35:41 +0200 Subject: [PATCH 15/22] typo fixes --- basics/dispatching.md | 6 +++--- datastorage/cache.md | 4 ++-- datastorage/cookie.md | 2 +- datastorage/session.md | 4 ++-- example_module/module.md | 2 +- frontend/styles_and_layout.md | 15 ++++++++------- general/setup.md | 2 +- services/events.md | 8 ++++---- services/uri.md | 2 +- 9 files changed, 23 insertions(+), 22 deletions(-) diff --git a/basics/dispatching.md b/basics/dispatching.md index c199186..34a36e2 100644 --- a/basics/dispatching.md +++ b/basics/dispatching.md @@ -1,12 +1,12 @@ # Dispatching -The dispatching is the follow up on the routing. In the dispatcher the route destination get resolved and executed. Dispatching can be performed on module instance methods, static functions and anonymous functions. +The dispatching is the follow up on the routing. In the dispatcher the route destination gets resolved and executed. Dispatching can be performed on module instance methods, static functions and anonymous functions. The return of the `dispatch()` call is an array of all end point returns. ## Basic -The dispatcher accepts a string representation of the method or static function which should be dispatched, a closure which should be executed or an array of the above. +The dispatcher accepts a string representation of the method or static function which should be dispatched, a closure which should be executed or an array of the just mentioned options. The `dispatch()` function accepts additionally a variable amount of parameters which will be passed to the routed method/function. @@ -38,7 +38,7 @@ $dispatcher->dispatch(function($para1, $para2) { ... }, $staticToCallPara1, $sta ## Routing -The dispatcher accepts the resoults from the `route()` method of the router which is an array of routes. +The dispatcher accepts the results from the `route()` method of the router which is an array of routes. ```php $dispatcher->dispatch($router->route($request->getUri()->getRoute())); diff --git a/datastorage/cache.md b/datastorage/cache.md index 5010e72..2a154f2 100644 --- a/datastorage/cache.md +++ b/datastorage/cache.md @@ -1,6 +1,6 @@ ## Cache -For caching the `CacheManager` provides access to the caching systems in place. Out of the box the CacheManager supports and automatically initializes either Redis or Memcached depending on the client configuration. The caching is not mandatory and therfor shouldn't be missuesed as in-memory database. It is not necessary to check if Redis or Memcached are available the CacheManager automatically handles the caching based on their existence. +For caching the `CacheManager` provides access to the caching systems in place. Out of the box the CacheManager supports and automatically initializes either Redis or Memcached depending on the client configuration. The caching is not mandatory and therefor shouldn't be misused as in-memory database. It is not necessary to check if Redis or Memcached are available the CacheManager automatically handles the caching based on their existence. ### HTTP Cache @@ -9,7 +9,7 @@ By default only stylesheets, javascript and layout images as well as module imag Example usage for 30 days caching: ```php -$resposne->setHeader('Cache-Control', 'Cache-Control: max-age=2592000'); +$response->setHeader('Cache-Control', 'Cache-Control: max-age=2592000'); ``` In order to trigger a re-cache of stylesheets or javascript files make sure to update the version in the `Controller.php` file. This way version updates will result in a new virtual file uri and result in a re-cache. diff --git a/datastorage/cookie.md b/datastorage/cookie.md index 1c60aa0..2ca7691 100644 --- a/datastorage/cookie.md +++ b/datastorage/cookie.md @@ -2,6 +2,6 @@ ## PHP -Only use cookies when absolutely necessary. Most of the time session data or local storage is the prefered choice. The `CookieJar` class provides you with all the necessary functionality similar to the `SessionManager`. The super global `$_COOKIE` is also overwritten and shouldn't be used anywhere. +Only use cookies when absolutely necessary. Most of the time session data or local storage is the preferred choice. The `CookieJar` class provides you with all the necessary functionality similar to the `SessionManager`. The super global `$_COOKIE` is also overwritten and shouldn't be used anywhere. ## JavaScript diff --git a/datastorage/session.md b/datastorage/session.md index 307e97d..abe24b6 100644 --- a/datastorage/session.md +++ b/datastorage/session.md @@ -1,6 +1,6 @@ # Sessions -Sessions are handled via the `SessionManager`. Sessions can be set and manipulated from the web application as well as the socket or console application. +Sessions are handled via the `SessionManager`. Sessions can be set and manipulated from the web application as well as the socket or console application. ## HTTP @@ -8,4 +8,4 @@ The Http session will be saved automatically, there is no need to access the sup ## Socket & Console -The session will be stored and assoziated with the logged in user in memory. A disconnect or quit is considered as a logout and therefor results in the destruction of the session object of this user and will be empty for the next login. +The session will be stored and associated with the logged in user in memory. A disconnect or quit is considered as a logout and therefor results in the destruction of the session object of this user and will be empty for the next login. diff --git a/example_module/module.md b/example_module/module.md index 98bd54d..c99d2c8 100644 --- a/example_module/module.md +++ b/example_module/module.md @@ -157,7 +157,7 @@ final class Installer extends InstallerAbstract } ``` -If your application doesn't need to implement any database tables for itself the switch statement can be omitted. From the directory structur at the beginning we can however see that some modules accept information form other modules. The following example shows how the navigation module is accepting information during the installation of other modules: +If your application doesn't need to implement any database tables for itself the switch statement can be omitted. From the directory structure at the beginning we can however see that some modules accept information form other modules. The following example shows how the navigation module is accepting information during the installation of other modules: ```php public static function installExternal(Pool $dbPool, array $data) diff --git a/frontend/styles_and_layout.md b/frontend/styles_and_layout.md index c543157..dc5b7bd 100644 --- a/frontend/styles_and_layout.md +++ b/frontend/styles_and_layout.md @@ -11,7 +11,7 @@ Flexboxes are preferred for all content containers. ```html
-
...
+
...
``` @@ -26,7 +26,7 @@ A container (e.g. section, div, table, etc) can be sized by using `.wf-*` classe ## Icons -This project uses font-awesome for its icons, the following example allows for stacked icons e.g. creating new/undread email notifications: +This project uses font-awesome for its icons, the following example allows for stacked icons e.g. creating new/unread email notifications: ```html @@ -49,7 +49,7 @@ The following snippet creates a 100% input with a button next to it. ### Input with dictionary -The following snippet creates a dictionary button (e.g. for opening a popup window to search for accounts/groups etc) right befor an input field. +The following snippet creates a dictionary button (e.g. for opening a popup window to search for accounts/groups etc) right before an input field. ```html @@ -63,18 +63,19 @@ The following snippet creates a dictionary button (e.g. for opening a popup wind A section is a container for information that can and should be grouped together. ```html -
-

Title

-
+
+
Title
+
...
+
Footer
``` Additional coloring of sections can be achieved by adding a coloring class. ```html -
+
...
``` diff --git a/general/setup.md b/general/setup.md index 4b21a31..0e74bfe 100644 --- a/general/setup.md +++ b/general/setup.md @@ -35,7 +35,7 @@ The following steps will setup the application, download all necessary tools and After the installation you'll have access to the following content: * Application: `http://127.0.0.1` -* Code Coverager: `http://127.0.0.1/Build/coverage/` +* Code Coverage: `http://127.0.0.1/Build/coverage/` * Test Report: `${INSPECTION_PATH}/test/ReportExternal` * Unit Test Report: `${INSPECTION_PATH}/test/testdox.txt` * PHPStan Report: `${INSPECTION_PATH}/test/phpstan.json` diff --git a/services/events.md b/services/events.md index c9aa8fc..58a2840 100644 --- a/services/events.md +++ b/services/events.md @@ -22,7 +22,7 @@ Now the event will be removed from the event manager once executed. ### Resetting events -In case an event should be reset (all conditions mut be met again befor it can be triggered) after it got successfully triggered one more parameter can be added. +In case an event should be reset (all conditions must be met again before it can be triggered) after it got successfully triggered one more parameter can be added. ```php $eventManager->attach('eventId', function() { echo 'Hello World'; }, false, true); @@ -40,7 +40,7 @@ $eventManager->trigger('eventId'); ## Multi Condition Events -In some cases it is required that multiple conditions are met before an event is supposed to be triggered. This can be achived by registering these conditions through the `addGroup()` function. +In some cases it is required that multiple conditions are met before an event is supposed to be triggered. This can be achieved by registering these conditions through the `addGroup()` function. ```php $eventManager->addGroup('eventId', 'conditionName'); @@ -58,8 +58,8 @@ $eventManager->trigger('eventId', 'conditionName'); // No output (if remove = fa $eventManager->trigger('eventId', 'conditionName'); // No output (if remove = true) ``` -The order in which these conditions are triggered doesn't mapper. A multi condition event SHOULD be atteched with the optional boolean parameter `true`. These events can only be executed once and will be removed afterwards. In case the optional boolean parameter was not set to `true` the event will remain in the event manager and will be triggered whenever `trigger('eventId')` is called. +The order in which these conditions are triggered doesn't mapper. A multi condition event SHOULD be attached with the optional boolean parameter `true`. These events can only be executed once and will be removed afterwards. In case the optional boolean parameter was not set to `true` the event will remain in the event manager and will be triggered whenever `trigger('eventId')` is called. ## Frontend vs. Backend -The only key difference between the frontend and backend implementation is that the frontend prevents runnning the same event in quick succession (less than 500 ms) in order to prevent undesired effects which can happen due to quick UI interaction. +The only key difference between the frontend and backend implementation is that the frontend prevents running the same event in quick succession (less than 500 ms) in order to prevent undesired effects which can happen due to quick UI interaction. diff --git a/services/uri.md b/services/uri.md index e34d900..0454145 100644 --- a/services/uri.md +++ b/services/uri.md @@ -46,7 +46,7 @@ Some default parameters can be used for easier use. While it's also possible to define parameters at the frontend and make use of the default values certain prefixes have a special meaning. -* \#somid = value of the element with the specified id +* \#someid = value of the element with the specified id * .somclass = values of the elements with the specified class ### Dynamic Parameters From f27feb766ce1970e4811d33c9cc4daffc1b4da01 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Sat, 11 Jul 2020 21:40:06 +0200 Subject: [PATCH 16/22] add checkbox and radio html example --- frontend/styles_and_layout.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/frontend/styles_and_layout.md b/frontend/styles_and_layout.md index dc5b7bd..0919cad 100644 --- a/frontend/styles_and_layout.md +++ b/frontend/styles_and_layout.md @@ -58,6 +58,29 @@ The following snippet creates a dictionary button (e.g. for opening a popup wind ``` +### Checkbox + +The following snippet creates a checkbox. + +```html + +``` + +### Radio + +The following snippet creates a checkbox. + +```html + Date: Fri, 31 Jul 2020 15:39:20 +0200 Subject: [PATCH 17/22] undo reset --- .gitignore | 1 + general/setup.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..22d0d82 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +vendor diff --git a/general/setup.md b/general/setup.md index 0e74bfe..14baf66 100644 --- a/general/setup.md +++ b/general/setup.md @@ -7,7 +7,7 @@ In order to setup the application for development for the first time please see Make sure your dev-environment or server fulfills the following requirements: * PHP >= 7.4 -* PHP extensions: mbstring, gd, zip, dom, mysql/pgsql/sqlsrv, sqlite, bcmath, imap*, redis*, memcached*, ftp*, socket*, curl*, xml* +* PHP extensions: mbstring, gd, zip, dom, mysql/pgsql/sqlsrv, sqlite, bcmath, imap\*, redis\*, memcached\*, ftp\*, socket\*, curl\*, xml\* * databases: mysql, postgresql, sqlsrv * webserver: apache2 * mod_headers From ec49a1be588073f9a7b03c5e7b40a15de346ed00 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Thu, 27 Aug 2020 18:54:24 +0200 Subject: [PATCH 18/22] link fixes --- SUMMARY.md | 80 +++++++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/SUMMARY.md b/SUMMARY.md index 6727d03..d5f23f1 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -1,58 +1,58 @@ # General -* [Introduction]({%}?page=README) -* [Setup]({%}?page=general/setup) -* [Application Structure]({%}?page=general/structure) +* [Introduction]({%}&page=README) +* [Setup]({%}&page=general/setup) +* [Application Structure]({%}&page=general/structure) ## Quality & Standards -* [Code Inspection]({%}?page=quality/inspections) -* [Documentation Standards]({%}?page=standards/documentation) -* [General Code Standards]({%}?page=standards/general) -* [Security Standards]({%}?page=security/security_guidelines) -* [Html Coding Standards]({%}?page=standards/html) -* [Php Coding Standards]({%}?page=standards/php) +* [Code Inspection]({%}&page=quality/inspections) +* [Documentation Standards]({%}&page=standards/documentation) +* [General Code Standards]({%}&page=standards/general) +* [Security Standards]({%}&page=security/security_guidelines) +* [Html Coding Standards]({%}&page=standards/html) +* [Php Coding Standards]({%}&page=standards/php) ## Examples -* [Application Sample]({%}?page=example_app/app) -* [Modules]({%}?page=example_module/module) -* [Packages]({%}?page=example_module/packages) +* [Application Sample]({%}&page=example_app/app) +* [Modules]({%}&page=example_module/module) +* [Packages]({%}&page=example_module/packages) ## API ### Message -* [Uri]({%}?page=services/uri) -* [Requests]({%}?page=basics/requests) -* [Responses]({%}?page=basics/responses) -* [Mail]({%}?page=services/mail) +* [Uri]({%}&page=services/uri) +* [Requests]({%}&page=basics/requests) +* [Responses]({%}&page=basics/responses) +* [Mail]({%}&page=services/mail) ### DataStorage -* [LocalStorage]({%}?page=datastorage/localstorage) -* [Database Connection]({%}?page=datastorage/database/connection) -* [DataMapper]({%}?page=datastorage/database/datamapper) -* [Queries]({%}?page=datastorage/database/queries) -* [Cache]({%}?page=datastorage/cache) -* [Session]({%}?page=datastorage/session) -* [Cookie]({%}?page=datastorage/cookie) +* [LocalStorage]({%}&page=datastorage/localstorage) +* [Database Connection]({%}&page=datastorage/database/connection) +* [DataMapper]({%}&page=datastorage/database/datamapper) +* [Queries]({%}&page=datastorage/database/queries) +* [Cache]({%}&page=datastorage/cache) +* [Session]({%}&page=datastorage/session) +* [Cookie]({%}&page=datastorage/cookie) ### System -* [Filesystem]({%}?page=services/filesystem) -* [Events]({%}?page=services/events) -* [Logging]({%}?page=services/logging) -* [Tasks]({%}?page=services/tasks) +* [Filesystem]({%}&page=services/filesystem) +* [Events]({%}&page=services/events) +* [Logging]({%}&page=services/logging) +* [Tasks]({%}&page=services/tasks) ### StdLib -* [Localization]({%}?page=services/localization) -* [Money]({%}?page=services/money) -* [Queues]({%}?page=services/queues) -* [Collection]({%}?page=services/collection) +* [Localization]({%}&page=services/localization) +* [Money]({%}&page=services/money) +* [Queues]({%}&page=services/queues) +* [Collection]({%}&page=services/collection) ### UI -* [Styles and Layout]({%}?page=frontend/styles_and_layout) -* [Charting]({%}?page=services/charting) -* [Codes]({%}?page=services/codes) -* [Routing]({%}?page=basics/routing) -* [Dispatching]({%}?page=basics/dispatching) -* [Views]({%}?page=basics/views) -* [Validation]({%}?page=services/validation) +* [Styles and Layout]({%}&page=frontend/styles_and_layout) +* [Charting]({%}&page=services/charting) +* [Codes]({%}&page=services/codes) +* [Routing]({%}&page=basics/routing) +* [Dispatching]({%}&page=basics/dispatching) +* [Views]({%}&page=basics/views) +* [Validation]({%}&page=services/validation) ### Security -* [Encoding]({%}?page=services/encoding) -* [Encryption]({%}?page=services/encryption) \ No newline at end of file +* [Encoding]({%}&page=services/encoding) +* [Encryption]({%}&page=services/encryption) \ No newline at end of file From 15783051bffea2f09f0788bfca1bfcbfd6bb13b2 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Thu, 8 Oct 2020 20:19:38 +0200 Subject: [PATCH 19/22] add preferred function --- standards/php.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/standards/php.md b/standards/php.md index 840b569..9427b9d 100644 --- a/standards/php.md +++ b/standards/php.md @@ -73,6 +73,12 @@ Class constants MUST have a access modifier public CONST_NAME = ...; ``` +## Preferred Functions + +### file_exists + +Instead of using `\file_exists()` the functions `\is_dir()` or `\is_file()` should be used. + ## Deprecated functions and variables The following functions and (super-) global variables MUST NOT be used. From 8c626d3cabd373d71525e96859fdeeaed4ed60d2 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Sun, 15 Nov 2020 21:11:05 +0100 Subject: [PATCH 20/22] start imporving docs --- general/app_flow.drawio.svg | 268 ++++++++++++++++++ general/app_flow.svg | 2 - general/{base_uml.svg => base_uml.drawio.svg} | 0 general/structure.md | 6 +- quality/inspections.md | 40 ++- quality/release.md | 0 standards/documentation.md | 8 + 7 files changed, 311 insertions(+), 13 deletions(-) create mode 100644 general/app_flow.drawio.svg delete mode 100644 general/app_flow.svg rename general/{base_uml.svg => base_uml.drawio.svg} (100%) create mode 100644 quality/release.md diff --git a/general/app_flow.drawio.svg b/general/app_flow.drawio.svg new file mode 100644 index 0000000..d5d0ad1 --- /dev/null +++ b/general/app_flow.drawio.svg @@ -0,0 +1,268 @@ + + + + + + + + + +
+
+
+ User +
+
+
+
+ + User + +
+
+ + + + + + +
+
+
+ Http Request +
+
+
+
+ + Http Request + +
+
+ + + + + + +
+
+
+ WebApp +
+
+
+
+ + WebApp + +
+
+ + + + + + + + + + + + +
+
+
+ ??? +
+ Application +
+
+
+
+ + ???... + +
+
+ + + + +
+
+
+ Router +
+
+
+
+ + Router + +
+
+ + + + + + + +
+
+
+ Dispatcher +
+
+
+
+ + Dispatcher + +
+
+ + + + + + + + + +
+
+
+ Modules / +
+ Controller +
+
+
+
+ + Modules /... + +
+
+ + + + + + + + + + +
+
+
+ Mapper +
+
+
+
+ + Mapper + +
+
+ + + + +
+
+
+ Database +
+
+
+
+ + Database + +
+
+ + + + + + +
+
+
+ View +
+
+
+
+ + View + +
+
+ + + + +
+
+
+ Template +
+
+
+
+ + Template + +
+
+ + + + +
+
+
+ Response +
+
+
+
+ + Response + +
+
+ + + + +
+
+
+ Model +
+
+
+
+ + Model + +
+
+
+ + + + + Viewer does not support full SVG 1.1 + + + +
\ No newline at end of file diff --git a/general/app_flow.svg b/general/app_flow.svg deleted file mode 100644 index a9d9497..0000000 --- a/general/app_flow.svg +++ /dev/null @@ -1,2 +0,0 @@ - -
User
User
Http Request
Http Request
WebApp
WebApp
???
Application
???<br>Application
Router
Router
Dispatcher
Dispatcher
Modules /
Controller
[Not supported by viewer]
Mapper
Mapper
Database
Database
View
View
Template
Template
Response
Response
\ No newline at end of file diff --git a/general/base_uml.svg b/general/base_uml.drawio.svg similarity index 100% rename from general/base_uml.svg rename to general/base_uml.drawio.svg diff --git a/general/structure.md b/general/structure.md index 692d27b..84da957 100644 --- a/general/structure.md +++ b/general/structure.md @@ -4,7 +4,7 @@ The user request gets passed through the entire application to all modules. The The routes usually reference endpoints in the module `controllers` which collects the model data through the model `mapper` and creates a partial response `view` with an assigned `template` and the collected model data. -![Application Flow](Developer-Guide/general/app_flow.svg) +![Application Flow](Developer-Guide/general/app_flow.drawio.svg) In the following only the WebApplication and Application are mentioned as the other components are explained in detail in their respective documentation. @@ -31,6 +31,6 @@ Furthermore the Application also performs a `CSRF` check, defines the `CSP`, aut ## Database -A short extract of the database structure can be seen below. Please note that this only contains the very basic tables from a fresh install without very limited modules and even then we only included the key tables for simplicity reasons. +A short extract of the database structure can be seen below. Please note that this only contains the very basic tables from a fresh install with very few modules and even then we only included the key tables for simplicity reasons. -![Application Flow](Developer-Guide/general/base_uml.svg) \ No newline at end of file +![Application Flow](Developer-Guide/general/base_uml.drawio.svg) \ No newline at end of file diff --git a/quality/inspections.md b/quality/inspections.md index 2170c44..31e252e 100644 --- a/quality/inspections.md +++ b/quality/inspections.md @@ -1,6 +1,30 @@ -# Inspections +# Code Inspections & Tests -Code inspections are very important in order to maintain the same code quality throughout the application. The Build repository contains all esential configuration files for the respective inspection tools. Every provided module will be evaluated based on the predefined code and quality standards. Only modules that pass all code, quality and unit tests are accepted. This also applies to updates and bug fixes. Any change will have to be re-evaluated. +Code inspections are very important in order to maintain the same code quality throughout the application. The `Build` repository and package managers such as `composer` and `npm` contain all esential configuration files for the respective inspection tools. The framework and every module will be evaluated based on the defined code and quality standards. Only code that passes all code, quality and test standards are accepted. Updates and bug fixes also must follow the standards. + +## How and what to test? + +In this project multiple levels of tests must be implemented such as unit tests, integration tests and system tests. + +The following testing requirements must be met: + +* 90% code coverage in the tests +* all tests must pass without warnings, errors and exceptions +* no warnings and errors during static code inspections +* no usage of deprecated function calls +* no code style violations +* every test should have a short description for the test report + +### Unit tests + +Every test must be in it's own test function. +Every public function must have a unit test + +### Integration tests + +### System tests + +## Test documentation ## Tools @@ -14,7 +38,7 @@ Tools used for the code inspection are: These tools are all installed by running the `setup.sh` script from the Build repository. -## PHPUnit +### PHPUnit This application uses PHPUnit as unit testing framework. Unit tests for specific classes need to be named in the same manner as the testing class. @@ -30,11 +54,11 @@ In order to also create a code coverage report run: php vendor/bin/phpunit -c tests/PHPUnit/phpunit_default.xml ``` -### Modules +#### Modules Every module needs to have a `Admin` directory containing a class called `AdminTest.php` which is used for testing the installation, activation, deactivation, uninstall and remove of the module. Tests that install, update, remove etc. a module need to have a group called `admin`. After running the `AdminTest.php` test the final state of the module should be installed and active, only this way it's possible to further test the controller and models. A code coverage of at least 80% is mandatory for every module for integration. -## PHPStan +### PHPStan With phpstan the code base is statically analyzed based on its configuration. This will help you to follow some of the "best" practices we enforce. @@ -42,11 +66,11 @@ With phpstan the code base is statically analyzed based on its configuration. Th php vendor/bin/phpstan analyse --autoload-file=phpOMS/Autoloader.php -l 8 -c Build/Config/phpstan.neon --error-format=prettyJson ./ > Build/test/phpstan.json ``` -## Jasmine +### Jasmine The javascript testing is done with jasmine. The javascript testing directory is structured the same way as the `Framework`. Unit tests for specific classes need to be named in the same manner as the testing class. -## PHP CS +### PHP CS Besides the code tests and static code analysis the code style is another very imporant inspection to ensure the code quality. @@ -54,7 +78,7 @@ Besides the code tests and static code analysis the code style is another very i php vendor/bin/phpcs ./ --standard="Build/Config/phpcs.xml" -s --report-junit=Build/test/junit_phpcs.xml ``` -## Git Hooks (Linux only) +### Git Hooks (Linux only) The git hooks perform various checks and validations during the `commit` and warn the developer about invalid code or code style/guideline violations. diff --git a/quality/release.md b/quality/release.md new file mode 100644 index 0000000..e69de29 diff --git a/standards/documentation.md b/standards/documentation.md index 5c28f53..11b4466 100644 --- a/standards/documentation.md +++ b/standards/documentation.md @@ -1,5 +1,13 @@ # Documentation +## User + +## Developer + +## Database + +### Diagramms + ## Php The php documentation is based on PhpDocumentor, therefore only valid PhpDocumentor comments are valid for files, classes, functions/methods and (member) variables. From 068a633716ff02663fbe81efdc9e702b788d5bc4 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Sat, 21 Nov 2020 23:58:01 +0100 Subject: [PATCH 21/22] improve docs --- SUMMARY.md | 2 +- quality/inspections.md | 29 +++++++++++++++++++++++++++-- standards/documentation.md | 26 ++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/SUMMARY.md b/SUMMARY.md index d5f23f1..9b36334 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -4,7 +4,7 @@ * [Application Structure]({%}&page=general/structure) ## Quality & Standards -* [Code Inspection]({%}&page=quality/inspections) +* [Code Inspections & Tests]({%}&page=quality/inspections) * [Documentation Standards]({%}&page=standards/documentation) * [General Code Standards]({%}&page=standards/general) * [Security Standards]({%}&page=security/security_guidelines) diff --git a/quality/inspections.md b/quality/inspections.md index 31e252e..d2feed9 100644 --- a/quality/inspections.md +++ b/quality/inspections.md @@ -14,18 +14,43 @@ The following testing requirements must be met: * no usage of deprecated function calls * no code style violations * every test should have a short description for the test report +* every file containing code (except enums, traits, interfaces and template files) must have their own test file with tests + +When testing it makes sense to test for the happy path/branch of how a method should work and to `try` and break things by trying to find inputs and paths which may lead to unexpected behavior and errors. ### Unit tests -Every test must be in it's own test function. -Every public function must have a unit test +The smallest/lowest level of testing are the unit tests. Unit tests should be implemented for models and framework methods. Every public function should be covered by at least one unit test. If a method has multiple branches every branch should be covered by a separate unit test. In some cases it might make sense to cover multiple branches in one unit test/test function, such a decision should however be made conciously. ### Integration tests +Integration tests are the second level or middle level of tests. These types of tests are used for example for module controllers. In such tests you should mock a request and test the response. + +For large controllers it can make sense to define a `*ControllerTest` which uses `Traits` in order to categorize different suits of tests. For example in the `Admin` ApiControllerTest we implemented different traits for group, account and module tests. + ### System tests +The system tests are the highest level of tests and test the overall functionality and if the implementation fulfills the specifications. + +### Modules + +Every module must implement the following tests if applicable: + +* general module tests (e.g. install, update, delete, status change) +* admin tests (`use \Modules\tests\ModuleTestTrait;`) +* model tests (unit tests) +* controller tests (e.g. ApiController tests) +* view tests + +You can find an example in the TestModule. + ## Test documentation +* every test must have a short test description +* every test and description must be added to the test report +* every test needs a test category (e.g. framework, module etc.) +* every test should have a @covers annotation to specify which class it covers + ## Tools Tools used for the code inspection are: diff --git a/standards/documentation.md b/standards/documentation.md index 11b4466..5fbc107 100644 --- a/standards/documentation.md +++ b/standards/documentation.md @@ -1,13 +1,35 @@ # Documentation -## User +## Module -## Developer +Every module must have two types of documentation. One for the end-user, which also includes information for the administrator and module configuration and another documentation for developers. Documentation must be available in at least english language. + +### User + +The user documentation should cover the following aspects: + +* Installation, update, status change and delete process +* Dependencies and requirements +* Configuration options +* Permission management +* How to use the module and its functionality from an end-user perspective + +### Developer + +The developer documentation should cover the following aspects: + +* Database UML diagram (incl. relations to other modules) +* How to interact with the controllers, views and models from other modules +* How to interact with the controllers, views and models from within the module +* Best practices (does and don'ts) +* Module specific guidelines & contribution information ## Database ### Diagramms +Every module that has a database table must implement a UML diagram illustrating the internal table relations as well as the relations with external tables from other modules. + ## Php The php documentation is based on PhpDocumentor, therefore only valid PhpDocumentor comments are valid for files, classes, functions/methods and (member) variables. From fb84c21b761044604a7a6aa0e9366cd0c61f6923 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Tue, 24 Nov 2020 17:31:19 +0100 Subject: [PATCH 22/22] remove some getter/setter --- basics/dispatching.md | 4 ++-- datastorage/cache.md | 2 +- example_app/app.md | 12 ++++++------ security/security_guidelines.md | 12 ++++++------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/basics/dispatching.md b/basics/dispatching.md index 34a36e2..7990628 100644 --- a/basics/dispatching.md +++ b/basics/dispatching.md @@ -41,11 +41,11 @@ $dispatcher->dispatch(function($para1, $para2) { ... }, $staticToCallPara1, $sta The dispatcher accepts the results from the `route()` method of the router which is an array of routes. ```php -$dispatcher->dispatch($router->route($request->getUri()->getRoute())); +$dispatcher->dispatch($router->route($request->uri->getRoute())); ``` Based on the function definition returned by the router it's possible to pass more parameters to the function such e.g. request and response objects. ```php -$dispatcher->dispatch($router->route($request->getUri()->getRoute()), $request, $response); +$dispatcher->dispatch($router->route($request->uri->getRoute()), $request, $response); ``` diff --git a/datastorage/cache.md b/datastorage/cache.md index 2a154f2..ab01c3e 100644 --- a/datastorage/cache.md +++ b/datastorage/cache.md @@ -17,5 +17,5 @@ In order to trigger a re-cache of stylesheets or javascript files make sure to u Example usage: ```php -$head->addAsset(AssetType::JS, $request->getUri()->getBase() . 'Modules/Media/Controller.js?v=' . self::MODULE_VERSION); +$head->addAsset(AssetType::JS, $request->uri->getBase() . 'Modules/Media/Controller.js?v=' . self::MODULE_VERSION); ``` \ No newline at end of file diff --git a/example_app/app.md b/example_app/app.md index 39f866a..6bea8f1 100644 --- a/example_app/app.md +++ b/example_app/app.md @@ -78,7 +78,7 @@ class Application extends ApplicationAbstract /* get data from url endpoints defined by the routes */ $dispatch = $this->dispatcher->dispatch( $this->router->route( - $request->getUri()->getRoute(), + $request->uri->getRoute(), $request->getData('CSRF'), // optional: only required if csrf tokens are used otherwise use null $request->getRouteVerb() // e.g. get, post, put ... ), @@ -91,7 +91,7 @@ class Application extends ApplicationAbstract $pageView->addData('dispatch', $dispatch); // push the headers (no changes to the header are possible afterwards) - $response->getHeader()->push(); + $response->header->push(); // renders the content of the response object (depends on the content type, text/html, json, ...) return $response->getBody(); @@ -107,10 +107,10 @@ class Application extends ApplicationAbstract $request->createRequestHashs(0); // if your application is located in a web-subfolder for easier handling - $request->getUri()->setRootPath('/'); + $request->uri->setRootPath('/'); // this will allow you to create urls based on request data - UriFactory::setupUriBuilder($request->getUri()); + UriFactory::setupUriBuilder($request->uri); return $request; } @@ -121,9 +121,9 @@ class Application extends ApplicationAbstract $response = new HttpResponse(); // you could use the request content-type in order to define the response content-type - $response->getHeader()->set('content-type', 'text/html; charset=utf-8'); + $response->header->set('content-type', 'text/html; charset=utf-8'); - $response->getHeader()->set('x-xss-protection', '1; mode=block'); + $response->header->set('x-xss-protection', '1; mode=block'); // more CSP can be defined here return $response; diff --git a/security/security_guidelines.md b/security/security_guidelines.md index e2bd528..e259f95 100644 --- a/security/security_guidelines.md +++ b/security/security_guidelines.md @@ -23,7 +23,7 @@ if($request->getData('CSRF') === null) { $response->setStatusCode(RequestStatus::R_403); /* optional */ - $response->set($request->getUri()->__toString(), new Notify('Unknown referrer!', NotifyType::INFO)); + $response->set($request->uri->__toString(), new Notify('Unknown referrer!', NotifyType::INFO)); return; } @@ -56,13 +56,13 @@ Scripts and frames must be provided by the own server or google. This is importa The default CSP looks like the following: ```php -$response->getHeader()->set('content-security-policy', 'script-src \'self\'; frame-src \'self\'', true); +$response->header->set('content-security-policy', 'script-src \'self\'; frame-src \'self\'', true); ``` In order to whitelist inline javascript you can use the following logic. This however requires you to know the inline script beforehand `$script`. After setting the CSP header they automatically get locked so that further changes are not possible. This is a security measure in order to prevent any malicious adjustments. ```php -$response->getHeader()->set('content-security-policy', 'script-src \'self\' \'sha256-' . base64_encode(hash('sha256', $script, true)) . '\'; frame-src \'self\'', true); +$response->header->set('content-security-policy', 'script-src \'self\' \'sha256-' . base64_encode(hash('sha256', $script, true)) . '\'; frame-src \'self\'', true); ``` ### X-XSS-Protection @@ -70,7 +70,7 @@ $response->getHeader()->set('content-security-policy', 'script-src \'self\' \'sh This header tells the client browser to use local xss protection if available. ```php -$response->getHeader()->set('x-xss-protection', '1; mode=block'); +$response->header->set('x-xss-protection', '1; mode=block'); ``` ### X-Content-Type-Options @@ -78,7 +78,7 @@ $response->getHeader()->set('x-xss-protection', '1; mode=block'); By using this header browsers which support this feature will ignore the content/mime and recognize the file by the provided header only. ```php -$response->getHeader()->set('x-content-type-options', 'nosniff'); +$response->header->set('x-content-type-options', 'nosniff'); ``` ### X-Frame-Options @@ -86,7 +86,7 @@ $response->getHeader()->set('x-content-type-options', 'nosniff'); The x-frame-options is providing the same protection for frames as the content-security-policy header. Please only use this header in addition to the content-security-policy if you have to but make sure the rules don't contradict with the content-security-policy. ```php -$response->getHeader()->set('x-frame-options', 'SAMEORIGIN'); +$response->header->set('x-frame-options', 'SAMEORIGIN'); ``` ## Superglobals