add documentation, fixes

pull/3/head
maxime.batista 2 years ago
parent 2e75aa780f
commit c63ca627d2

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

@ -0,0 +1,218 @@
This documentation file explains how to start a development server on your
machine, and how it works under the hood.
# How to run the project on my local computer
1) Use phpstorm to run a local php server:
* Go to configuration > add new configuration
* Select "PHP Built-in Web Server", then enter options as follow:
![](assets/php-server-config.png)
- port 8080
- 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.
This is expected ! On your browser, open inspection tab (ctrl+shift+i) go to network and refresh the page.
![](assets/browser-network-inspection.png)
We can see that the browser requested the `SampleForm.tsx` react view (located in `/front/views`), but the server could not find it.
But, on what server was it requested ?
Remember that the `localhost:8080` is a _php_ server, and thus not able to handle requests about our react / typescript files.
If we take a look at the request, we'll see that the url does not targets `localhost:8080`, but `localhost:5173`.
![](assets/SampleForm.tsx-request-404.png)
`localhost:5173` is the react development server, it is able to serve our react front view files.
Let's run the react development server.
It is a simple as running `npm start` in a new terminal (be sure to run it in the repository's directory).
![](assets/npm-start.png)
You should see something like this, it says that the server was opened on port `5173`, thats our react development server !
Now refresh your page, you should now see all request being fulfilled and a form appearing !
![](assets/form-rendered.png)
Caution: **NEVER** directly connect on the `localhost:5173` node development server, always pass through the php (`localhost:8080`) server.
# How it works
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 is our gateway, it uses an `AltoRouter` that dispatches the request's process 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()`).
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`
```php
require_once "react-display.php";
class SampleFormController {
public function displayForm() {
send_react_front("views/SampleForm.tsx", []);
}
public function displayResults(array $request) {
send_react_front("views/DisplayResults.tsx", $request);
}
}
```
As our views are now done using react (and defined under the `front/views` folder), we need to use the `send_react_front($viewURI, $viewArguments)` php function (located in the `src/react-render.php` file).
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 script block imports the requested view and will render it using a specific `render(any)` function.
This function __must__ figure in the imported view file in order to work.
```html
<!--
here's the magic.
imports the given view URL, and assume that the view exports a function named `render` that
takes exactly one object argument.
If the imported file does not export a function with signature `render(arguments: any)`,
the call wont work thus the view will not be displayed
-->
<script type="module">
import {render} from "<?= asset($url) ?>"
render(<?= json_encode($arguments) ?>)
</script>
```
here's how it renders if you do a request to `http://localhost:8080/`.
![](assets/render-react-php-file-processed.png)
The index.php's router says that for a `GET` on the `/` url, we call the `SampleFormController#displayForm` method.
This method then uses the `send_react_front`, to render the `views/SampleForm.tsx` react element, with no arguments (an empty array).
You can see that the react view is rendered using a `render` function. This function **must figure in the view's file, and be exported**.
We'll talk about an important coding convention about views that this script block implies later.
But now let's talk about our server profiles !
## Server Profiles
If you go on the staging server, you'll see that, for the exact same request equivalent, the generated `render-react-file` file changes :
![](assets/staging-server-render-react-php-file-processed.png)
(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.
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 has 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, you'll see that
it defines an `asset(string $uri)` function.
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 two profiles declares a `_asset(string $uri)` function, used by the `/config.php::asset` method, but with different implementations :
### Development profile
```php
const FRONT_URL_CONSTANT = "http://localhost:5173";
function _asset(string $assetURI): string {
return FRONT_URL_CONSTANT . "/" . $assetURI;
}
```
The simplest profile, simply redirect all assets to the development server
### Production profile
Before the CD deploys the generated files to the server,
it'll generate a `/views-mappings.php` file that will map the react views file names to their compiled javascript files :
```php
const ASSETS = [
// react / typescript path (relative to /front) => its compiled js file name.
'views/SampleForm.tsx' => 'front/views/SampleForm.tsx-82fdeb9a.js',
'views/DisplayResults.tsx' => 'front/views/DisplayResults.tsx-ed098cf4.js',
... // 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.
```php
require "../views-mappings.php";
function _asset(string $assetURI): string {
// use index.php's base path
global $basePath;
// If the asset uri does not figure in the available assets array,
// fallback to the uri itself.
return $basePath . "/" . (ASSETS[$assetURI] ?? $assetURI);
}
```
## React views conventions.
Conventions regarding our react views __must be respected in order to be renderable__.
### The `render(any)` function
__any react view file__ should __export__ a function with signature `render(arguments: any)`, which responsibility is to render the view.
The `arguments` parameter is used to pass data to the react component.
If you take a look at the `front/views/SampleForm.tsx` view, here's the definition of its render function :
```ts
/// Here's the definition of the view
function SampleForm() {
... react jsx code here
}
// the `SampleForm` does not inputs arguments but the parameter IS STILL REQUIRED
export function render(args: any) {
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<SampleForm /> //here's our view component
</React.StrictMode>
);
}
```
## Pass arguments to our react views
We'll take the example of the view in charge of displaying the SimpleForm's fields values.
First, let's take a look at its React Component definition :
```ts
function DisplayResults({password, username}: any) {
return (
<div>
<p>username: {username}</p> //arguments are used heres
<p>password: {password}</p>
</div>
)
}
```
The components takes two arguments, a `username` and `password` string.
Now, let's take a look at its `render` function :
```ts
export function render(args: any) {
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<DisplayResults username={args.name} password={args.password} />
</React.StrictMode>
);
}
```
let's take a look at this line:
```
<DisplayResults username={args.name} password={args.password} />
```
`DisplayResults` is the name of our React Component (the function).
To pass arguments to the `DisplayResults` function, we use the syntax `<function parameter name>={value}`. (somewhat like regular html/xml components).
Use the render's function `args` parameter to access to the php's array values (see below).
So now we have defined our react view, let's send it to the user's browser from php.
To render the `DisplayResults` view, we'll use the `send_react_front()`:
```php
require_once "react-display.php";
...
send_react_front(
"views/DisplayResults.tsx", // view file location, relative to the `/front` folder.
array("name" => "samuel barion", "password" => "1234") //our function's arguments
)
```
NOTE: in the project, the `$_POST` is placed instead of the hardcoded array, as the `$_POST` is an array containing the "name" and "password" keys. (regarding the SampleForm's form inputs names)).

@ -24,6 +24,7 @@ steps:
volumes: *outputs
commands:
- mkdir -p /outputs/public
# this sed command will replace the included `profile/dev-config-profile.php` to `profile/prod-config-file.php` in the config.php file.
- sed -iE 's/\\/\\*PROFILE_FILE\\*\\/\\s*".*"/"profiles\\/prod-config-profile.php"/' config.php
- composer install && composer update
- rm profiles/dev-config-profile.php

@ -5,12 +5,6 @@
// Please do not touch.
require /*PROFILE_FILE*/ "profiles/dev-config-profile.php";
/**
* The URL to prepend when accessing front-end resources,
* please prefer using `asset(url)` instead.
*/
const FRONT_URL = FRONT_URL_CONSTANT;
/**
* Maps the given relative source uri (relative to the `/front` folder) to its actual location depending on imported profile.
* @param string $assetURI relative uri path from `/front` folder

@ -1,9 +1,13 @@
<?php
// This file only exists on production servers, and defines the available assets mappings
// in an `ASSETS` array constant.
require "../views-mappings.php";
const FRONT_URL_CONSTANT = "/IQBall/public";
function _asset(string $assetURI): string {
return FRONT_URL_CONSTANT . "/" . (ASSETS[$assetURI] ?? $assetURI);
// use index.php's base path
global $basePath;
// If the asset uri does not figure in the available assets array,
// fallback to the uri itself.
return $basePath . "/" . (ASSETS[$assetURI] ?? $assetURI);
}

@ -5,15 +5,17 @@ require "../config.php";
use App\Controller\SampleFormController;
// find the base path of the index.php file
/**
* relative path of the index.php's directory from the server's document root.
*/
global $basePath;
// find the server path of the index.php file
$basePath = dirname(substr(__FILE__, strlen($_SERVER['DOCUMENT_ROOT'])));
if (str_ends_with($basePath, "/")) {
if ($basePath[strlen($basePath) - 1] == "/") {
$basePath = substr($basePath, 0, strlen($basePath) - 1);
}
// routes initialization
$router = new AltoRouter();
$router->setBasePath($basePath);

Loading…
Cancel
Save