- `src/API`, qui définit les classes qui implémentent les actions de l’api
- `src/API`, qui définit les classes qui implémentent les actions de l’api
- `src/App`, qui définit les contrôleurs et les vues de l’application web
- `src/App`, qui définit les contrôleurs et les vues de l’application web
- `src/Core`, définit les modèles, les classes métiers, les librairies internes (validation, http), les gateways, en somme, les élements logiques de l’application et les actions que l’ont peut faire avec.
- `src/Core`, définit les modèles, les classes métiers, les librairies internes (validation, http), les gateways, en somme, les élements logiques de l’application et les actions que l’ont peut faire avec.
- `sql`, définit la base de donnée utilisée, et éxécute les fichiers sql lorsque la base de donnée n’est pas initialisée.
- `sql`, définit la base de donnée utilisée, et éxécute les fichiers sql lorsque la base de donnée n’est pas initialisée.
- `profiles`, définit les profiles d’execution, voir `Documentation/how-to-dev.md` pour plus d’info
- `profiles`, définit les profiles d’execution, voir `Documentation/how-to-dev.md` pour plus d’info
- `front` contient le code front-end react/typescript
- `front` contient le code front-end react/typescript
- `ci` contient les scripts de déploiement et la définition du workflow d’intégration continue et de déploiement constant vers notre staging server ([maxou.dev/<branch>/public/](https://maxou.dev/IQBall/master/public)).
- `ci` contient les scripts de déploiement et la définition du workflow d’intégration continue et de déploiement constant vers notre staging server ([maxou.dev/<branch>/public/](https://maxou.dev/IQBall/master/public)).
- `public` point d’entrée, avec :
- `public` point d’entrée, avec :
- `public/index.php` point d’entrée pour la webapp
- `public/index.php` point d’entrée pour la webapp
- `public/api/index.php` point d’entrée pour l’api.
- `public/api/index.php` point d’entrée pour l’api.
## Backend
## Backend
### Validation et résilience des erreurs
### Validation et résilience des erreurs
#### Motivation
#### Motivation
Un controlleur a pour but de valider les données d'une requête avant de les manipuler.
Un controlleur a pour but de valider les données d'une requête avant de les manipuler.
Nous nous sommes rendu compte que la vérification des données d'une requête était redondante, même en factorisant les différents
Nous nous sommes rendu compte que la vérification des données d'une requête était redondante, même en factorisant les différents
types de validation, nous devions quand même explicitement vérifier la présence des champs utilisés dans la requête.
types de validation, nous devions quand même explicitement vérifier la présence des champs utilisés dans la requête.
```php
```php
public function doPostAction(array $form) {
public function doPostAction(array $form) {
@ -39,11 +41,11 @@ public function doPostAction(array $form) {
if (Validation::isLenBetween($email, 6, 64))) {
if (Validation::isLenBetween($email, 6, 64))) {
$failures[] = "L'adresse email doit être d'une longueur comprise entre 6 et 64 charactères.";
$failures[] = "L'adresse email doit être d'une longueur comprise entre 6 et 64 charactères.";
@ -56,9 +58,10 @@ Bien souvent, lorsque le prédicat échoue, un message est ajouté à la liste d
de la redondance sur les messages d'erreurs choisis lorsqu'une validation échoue.
de la redondance sur les messages d'erreurs choisis lorsqu'une validation échoue.
#### Schéma
#### Schéma
Toutes ces lignes de code pour définir que notre requête doit contenir un champ nommé `email`, dont la valeur est une adresse mail, d'une longueur comprise entre 6 et 64.
Toutes ces lignes de code pour définir que notre requête doit contenir un champ nommé `email`, dont la valeur est une adresse mail, d'une longueur comprise entre 6 et 64.
Nous avons donc trouvé l'idée de définir un système nous permettant de déclarer le schéma d'une requête,
Nous avons donc trouvé l'idée de définir un système nous permettant de déclarer le schéma d'une requête,
et de reporter le plus d'erreurs possibles lorsqu'une requête ne valide pas le schéma :
et de reporter le plus d'erreurs possibles lorsqu'une requête ne valide pas le schéma :
```php
```php
public function doPostAction(array $form): HttpResponse {
public function doPostAction(array $form): HttpResponse {
@ -66,17 +69,18 @@ public function doPostAction(array $form): HttpResponse {
Ce système nous permet de faire de la programmation _déclarative_, nous disons à _quoi_ ressemble la forme de la requête que l'on souhaite,
Ce système nous permet de faire de la programmation _déclarative_, nous disons à _quoi_ ressemble la forme de la requête que l'on souhaite,
plustot que de définir _comment_ réagir face à notre requête.
plustot que de définir _comment_ réagir face à notre requête.
Ce système permet d'être beaucoup plus clair sur la forme attendue d'une requête, et de garder une lisibilité satisfaisante peu importe le nombre de
Ce système permet d'être beaucoup plus clair sur la forme attendue d'une requête, et de garder une lisibilité satisfaisante peu importe le nombre de
champs que celle-ci contient.
champs que celle-ci contient.
Nous pouvons ensuite emballer les erreurs de validation dans des `ValidationFail` et `FieldValidationFail`, ce qui permet ensuite d'obtenir
Nous pouvons ensuite emballer les erreurs de validation dans des `ValidationFail` et `FieldValidationFail`, ce qui permet ensuite d'obtenir
@ -84,40 +88,41 @@ plus de précision sur une erreur, comme le nom du champ qui est invalidé, et q
les erreurs et facilement entourer les champs invalides en rouge, ainsi que d'afficher toutes les erreurs que l'utilisateur a fait, d'un coup.
les erreurs et facilement entourer les champs invalides en rouge, ainsi que d'afficher toutes les erreurs que l'utilisateur a fait, d'un coup.
### HttpRequest, HttpResponse
### HttpRequest, HttpResponse
Nous avons choisi d'encapsuler les requêtes et les réponses afin de faciliter leur manipulation.
Nous avons choisi d'encapsuler les requêtes et les réponses afin de faciliter leur manipulation.
Cela permet de mieux repérer les endroits du code qui manipulent une requête d'un simple tableau,
Cela permet de mieux repérer les endroits du code qui manipulent une requête d'un simple tableau,
et de garder à un seul endroit la responsabilitée d'écrire le contenu de la requête vers client.
et de garder à un seul endroit la responsabilitée d'écrire le contenu de la requête vers client.
`src/App` définit une `ViewHttpResponse`, qui permet aux controlleurs de retourner la vue qu'ils ont choisit.
`src/App` définit une `ViewHttpResponse`, qui permet aux controlleurs de retourner la vue qu'ils ont choisit.
C'est ensuite à la classe `src/App/App` d'afficher la réponse.
C'est ensuite à la classe `src/App/App` d'afficher la réponse.
### index.php
### index.php
Il y a deux points d'entrés, celui de la WebApp (`public/index.php`), et celui de l'API (`public/api/index.php`).
Il y a deux points d'entrés, celui de la WebApp (`public/index.php`), et celui de l'API (`public/api/index.php`).
Ces fichiers ont pour but de définir ce qui va être utilisé dans l'application, comme les routes, la base de donnée, les classes métiers utilisés,
Ces fichiers ont pour but de définir ce qui va être utilisé dans l'application, comme les routes, la base de donnée, les classes métiers utilisés,
comment gérer l'authentification dans le cadre de l'API, et une session dans le cadre de la web app.
comment gérer l'authentification dans le cadre de l'API, et une session dans le cadre de la web app.
L'index définit aussi quoi faire lorsque l'application retourne une réponse. Dans les implémentations actuelles, elle délègue simplement
L'index définit aussi quoi faire lorsque l'application retourne une réponse. Dans les implémentations actuelles, elle délègue simplement
l'affichage dans une classe (`IQBall\App\App` et `IQBall\API\API`).
l'affichage dans une classe (`IQBall\App\App` et `IQBall\API\API`).
### API
### API
Nous avons définit une petite API (`src/Api`) pour nous permettre de faire des actions en arrière plan depuis le front end.
Nous avons définit une petite API (`src/Api`) pour nous permettre de faire des actions en arrière plan depuis le front end.
Par exemple, l'API permet de changer le nom d'une tactique, ou de sauvegarder son contenu.
Par exemple, l'API permet de changer le nom d'une tactique, ou de sauvegarder son contenu.
C'est plus pratique de faire des requêtes en arrière-plan plustot que faire une requête directement à la partie WebApp, ce qui
C'est plus pratique de faire des requêtes en arrière-plan plustot que faire une requête directement à la partie WebApp, ce qui
aurait eu pour conséquences de recharger la page
aurait eu pour conséquences de recharger la page
## Frontend
## Frontend
### Utilisation de React
### Utilisation de React
Notre application est une application de création et de visualisation de stratégies pour des match de basket.
Notre application est une application de création et de visualisation de stratégies pour des match de basket.
L’éditeur devra comporter un terrain sur lequel il est possible de placer et bouger des pions, représentant les joueurs.
L’éditeur devra comporter un terrain sur lequel il est possible de placer et bouger des pions, représentant les joueurs.
Une stratégie est un arbre composé de plusieurs étapes, une étape étant constituée d’un ensemble de joueurs et d’adversaires sur le terrain,
Une stratégie est un arbre composé de plusieurs étapes, une étape étant constituée d’un ensemble de joueurs et d’adversaires sur le terrain,
aillant leur position de départ, et leur position cible, avec des flèches représentant le type de mouvement (dribble, écran, etc) effectué.
aillant leur position de départ, et leur position cible, avec des flèches représentant le type de mouvement (dribble, écran, etc) effectué.
les enfants d’une étape, sont d’autres étapes en fonction des cas de figures (si tel joueur fait tel mouvement, ou si tel joueur fait telle passe etc).
les enfants d’une étape, sont d’autres étapes en fonction des cas de figures (si tel joueur fait tel mouvement, ou si tel joueur fait telle passe etc).
Pour rendre le tout agréable à utiliser, il faut que l’interface soit réactive : si l’on bouge un joueur,
Pour rendre le tout agréable à utiliser, il faut que l’interface soit réactive : si l’on bouge un joueur,
il faut que les flèches qui y sont liés bougent aussi, il faut que les joueurs puissent bouger le long des flèches en mode visualiseur etc…
il faut que les flèches qui y sont liés bougent aussi, il faut que les joueurs puissent bouger le long des flèches en mode visualiseur etc…
Le front-end de l’éditeur et du visualiseur étant assez ambitieux, et occupant une place importante du projet, nous avons décidés de l’effectuer en utilisant
Le front-end de l’éditeur et du visualiseur étant assez ambitieux, et occupant une place importante du projet, nous avons décidés de l’effectuer en utilisant
le framework React qui rend simple le développement d’interfaces dynamiques, et d’utiliser typescript parce qu’ici on code bien et qu’on impose une type safety a notre code.
le framework React qui rend simple le développement d’interfaces dynamiques, et d’utiliser typescript parce qu’ici on code bien et qu’on impose une type safety a notre code.
As you can see our entire application is build around three main package.
As you can see our entire application is build around three main package.
@ -13,7 +14,7 @@ Allowing to operate on it.
The App now is more about the web application itself.
The App now is more about the web application itself.
Having all the controllers of the MVC architecture the use the model, the validation system and the http system in the core.
Having all the controllers of the MVC architecture the use the model, the validation system and the http system in the core.
It also calls the twig's views inside of App. Finally, it uses the package Session. This one replace the $_SESSION we all know in PHP.
It also calls the twig's views inside of App. Finally, it uses the package Session. This one replace the $\_SESSION we all know in PHP.
Thanks to this we have a way cleaner use of all session's data.
Thanks to this we have a way cleaner use of all session's data.
Nevertheless, all the controllers call not only twig views but also react ones.
Nevertheless, all the controllers call not only twig views but also react ones.
Those are present in the package "front", dispatched in several other packages.
Those are present in the package "front", dispatched in several other packages.
@ -22,10 +23,11 @@ Such as assets having all the image and stuff, model containing all the data's s
Finally, we have the package "Api" that allows to share code and bind all the different third-hand application such as the web admin one.
Finally, we have the package "Api" that allows to share code and bind all the different third-hand application such as the web admin one.
## Main data class diagram.
## Main data class diagram.


You can see how our data is structured contained in the package "data" as explained right above.
You can see how our data is structured contained in the package "data" as explained right above.
There is two clear part.
There is two clear part.
First of all, the Tactic one.
First of all, the Tactic one.
We got a nice class named TacticInfo representing as it says the information about a tactic, nothing to discuss more about.
We got a nice class named TacticInfo representing as it says the information about a tactic, nothing to discuss more about.
It associates an attribute of type "CourtType". This last is just an "evoluated" type of enum with some more features.
It associates an attribute of type "CourtType". This last is just an "evoluated" type of enum with some more features.
@ -42,6 +44,7 @@ The last class we have is the Account. It could directly be incorporated in User
Then, Account only has a user and a token which is an identifier.
Then, Account only has a user and a token which is an identifier.
## Validation's class diagram
## Validation's class diagram


We implemented our own validation system, here it is!
We implemented our own validation system, here it is!
@ -50,32 +53,38 @@ In general, we use the implementation "SimpleFunctionValidator".
We reconize the strategy pattern. Indeed, we need a family of algorithms because we have many classes that only differ by the way they validate.
We reconize the strategy pattern. Indeed, we need a family of algorithms because we have many classes that only differ by the way they validate.
Futhermore, you may have notices the ComposedValidator that allows to chain several Validator.
Futhermore, you may have notices the ComposedValidator that allows to chain several Validator.
We can see that this system uses the composite pattern
We can see that this system uses the composite pattern
The other part of the diagram is about the failure a specific field's validation.
The other part of the diagram is about the failure a specific field's validation.
We have a concrete class to return a something more general. All the successors are just more precise about the failure.
We have a concrete class to return a something more general. All the successors are just more precise about the failure.
## Http's class diagram
## Http's class diagram


It were we centralize what the app can render, and what the api can receive.
It were we centralize what the app can render, and what the api can receive.
Then, we got the "basic" response (HttpResponse) that just render a HttpCodes.
Then, we got the "basic" response (HttpResponse) that just render a HttpCodes.
We have two successors for now. ViewHttpResponse render not only a code but also a view, either react or twig ones.
We have two successors for now. ViewHttpResponse render not only a code but also a view, either react or twig ones.
Finally, we have the JsonHttpResponse that renders, as it's name says, some Json.
Finally, we have the JsonHttpResponse that renders, as it's name says, some Json.
## Session's class diagram
## Session's class diagram


It encapsulates the PHP's array "$_SESSION". With two interfaces that dictate how a session should be handled, and same for a mutable one.
It encapsulates the PHP's array "$\_SESSION". With two interfaces that dictate how a session should be handled, and same for a mutable one.
## Model View Controller
## Model View Controller
All class diagram, separated by their range of action, of the imposed MVC architecture.
All class diagram, separated by their range of action, of the imposed MVC architecture.
All of them have a controller that validates entries with the validation system and check the permission the user has,and whether or not actually do the action.
All of them have a controller that validates entries with the validation system and check the permission the user has,and whether or not actually do the action.
These controllers are composed by a Model that handle the pure data and is the point of contact between these and the gateways.
These controllers are composed by a Model that handle the pure data and is the point of contact between these and the gateways.
Speaking of which, Gateways are composing Models. They use the connection class to access the database and send their query.
Speaking of which, Gateways are composing Models. They use the connection class to access the database and send their query.
@ -3,17 +3,13 @@ machine, and how it works under the hood.
# How to run the project on my local computer
# How to run the project on my local computer
1. Use phpstorm to run a local php server:
1) Use phpstorm to run a local php server:
- Go to configuration > add new configuration
* Go to configuration > add new configuration
- Select "PHP Built-in Web Server", then enter options as follow:
* Select "PHP Built-in Web Server", then enter options as follow:
 - port 8080 - name the configuration "RunServer" to be more explicit - place the "Document Root" in `/public` - host is localhost

- Click apply, OK
- port 8080
- Now run it.
- name the configuration "RunServer" to be more explicit
- place the "Document Root" in `/public`
- host is localhost
* Click apply, OK
* Now run it.
If you go to `http://localhost:8080` you'll see a blank page.
If you go to `http://localhost:8080` you'll see a blank page.
This is expected ! On your browser, open inspection tab (ctrl+shift+i) go to network and refresh the page.
This is expected ! On your browser, open inspection tab (ctrl+shift+i) go to network and refresh the page.
@ -39,12 +35,14 @@ Now refresh your page, you should now see all request being fulfilled and a form
Caution: **NEVER** directly connect on the `localhost:5173` node development server, always pass through the php (`localhost:8080`) server.
Caution: **NEVER** directly connect on the `localhost:5173` node development server, always pass through the php (`localhost:8080`) server.
# How it works
# How it works
I'm glad you are interested in how that stuff works, it's a bit tricky, lets go.
I'm glad you are interested in how that stuff works, it's a bit tricky, lets go.
If you look at our `index.php` (located in `/public` folder), you'll see that it define our routes, it uses an `AltoRouter` then delegates the request's action processing to a controller.
If you look at our `index.php` (located in `/public` folder), you'll see that it define our routes, it uses an `AltoRouter` then delegates the request's action processing to a controller.
We can see that there are two registered routes, the `GET:/` (that then calls `SampleFormController#displayForm()`) and `POST:/result` (that calls `SampleFormController#displayResults()`).
We can see that there are two registered routes, the `GET:/` (that then calls `SampleFormController#displayForm()`) and `POST:/result` (that calls `SampleFormController#displayResults()`).
Implementation of those two methods are very simple: there is no verification to make nor model to control, thus they directly sends the view back to the client.
Implementation of those two methods are very simple: there is no verification to make nor model to control, thus they directly sends the view back to the client.
here's the implementation of the `SampleFormController`
here's the implementation of the `SampleFormController`
```php
```php
require_once "react-display.php";
require_once "react-display.php";
class SampleFormController {
class SampleFormController {
@ -63,7 +61,7 @@ As our views are now done using react (and defined under the `front/views` folde
If you look at the `send_react_front($viewURI, $viewArguments)` function, you'll see that is simply loads the file `src/react-display-file.php` with given arguments.
If you look at the `send_react_front($viewURI, $viewArguments)` function, you'll see that is simply loads the file `src/react-display-file.php` with given arguments.
The file is a simple html5 template with a `<script>` block in the `<body>` section.
The file is a simple html5 template with a `<script>` block in the `<body>` section.
The script block imports the requested view and will render it.
The script block imports the requested view and will render it.
The view entry is a function, named in PascalCase, which __must__ be be exported by default (`export default function MyReactView(args: {..})`).
The view entry is a function, named in PascalCase, which **must** be be exported by default (`export default function MyReactView(args: {..})`).
```html
```html
<!--
<!--
@ -72,35 +70,37 @@ imports the given view URL, and assume that the view exports a function named `C
see ViewRenderer.tsx::renderView for more info
see ViewRenderer.tsx::renderView for more info
-->
-->
<scripttype="module">
<scripttype="module">
import {renderView} from "<?= asset("ViewRenderer.tsx") ?>"
import {renderView} from "<?= asset("ViewRenderer.tsx") ?>"
(we can also see that much less files are downloaded than with our localhost aka development server).
(we can also see that much less files are downloaded than with our localhost aka development server).
Remember that react components and typescript files needs to be transpiled to javascript before being executable by a browser.
Remember that react components and typescript files needs to be transpiled to javascript before being executable by a browser.
The generated file no longer requests the view to a `localhost:5173` or a `maxou.dev:5173` server,
The generated file no longer requests the view to a `localhost:5173` or a `maxou.dev:5173` server,
now our react components are directly served by the same server, as they have been pre-compiled by our CI (see `/ci/.drone.yml` and `/ci/build_react.msh`) into valid js files that can directly be send to the browser.
now our react components are directly served by the same server, as they have been pre-compiled by our CI (see `/ci/.drone.yml` and `/ci/build_react.msh`) into valid js files that can directly be send to the browser.
If you go back to our `index.php` file, you'll see that it requires a `../config.php` file, if you open it,
If you go back to our `index.php` file, you'll see that it requires a `../config.php` file, if you open it,
you'll see that it defines the `asset(string $uri)` function that is used by the `src/react-display-file.php`,
you'll see that it defines the `asset(string $uri)` function that is used by the `src/react-display-file.php`,
in the `<script>` block we talked earlier.
in the `<script>` block we talked earlier.
By default, the `/config.php` file uses the `dev-config-profile.php` profile,
By default, the `/config.php` file uses the `dev-config-profile.php` profile,
the file is replaced with `prod-config-file.php` by the CI when deploying to the staging server (see the pipeline "prepare php" step in `/ci/.drone.yml`)
the file is replaced with `prod-config-file.php` by the CI when deploying to the staging server (see the pipeline "prepare php" step in `/ci/.drone.yml`)
The two profiles declares an `_asset(string $uri)` function, used by the `/config.php::asset` method, but with different implementations :
The two profiles declares an `_asset(string $uri)` function, used by the `/config.php::asset` method, but with different implementations :
### Development profile
### Development profile
```php
```php
@ -112,11 +112,13 @@ function _asset(string $assetURI): string {
return $front_url . "/" . $assetURI;
return $front_url . "/" . $assetURI;
}
}
```
```
The simplest profile, simply redirect all assets to the development server
The simplest profile, simply redirect all assets to the development server
### Production profile
### Production profile
Before the CD workflow step deploys the generated files to the server,
it generates a `/views-mappings.php` file that will map the react views file names to their compiled javascript files :
Before the CD workflow step deploys the generated files to the server,
it generates a `/views-mappings.php` file that will map the react views file names to their compiled javascript files :
```php
```php
const ASSETS = [
const ASSETS = [
@ -126,7 +128,9 @@ const ASSETS = [
... // other files that does not have to be directly used by the `send_react_front()` function
... // other files that does not have to be directly used by the `send_react_front()` function
];
];
```
```
The `_asset` function will then get the right javascript for the given typescript file.
The `_asset` function will then get the right javascript for the given typescript file.
```php
```php
require "../views-mappings.php";
require "../views-mappings.php";
@ -137,4 +141,4 @@ function _asset(string $assetURI): string {