After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 45 KiB |
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:
|
||||

|
||||
- 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.
|
||||

|
||||
|
||||
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`.
|
||||
|
||||

|
||||
|
||||
`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).
|
||||

|
||||
|
||||
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 !
|
||||
|
||||

|
||||
|
||||
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/`.
|
||||
|
||||

|
||||
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 :
|
||||

|
||||
(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)).
|
@ -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);
|
||||
}
|