diff --git a/Documentation/assets/SampleForm.tsx-request-404.png b/Documentation/assets/SampleForm.tsx-request-404.png new file mode 100644 index 0000000..9bae7e7 Binary files /dev/null and b/Documentation/assets/SampleForm.tsx-request-404.png differ diff --git a/Documentation/assets/browser-network-inspection.png b/Documentation/assets/browser-network-inspection.png new file mode 100644 index 0000000..3aff1ff Binary files /dev/null and b/Documentation/assets/browser-network-inspection.png differ diff --git a/Documentation/assets/form-rendered.png b/Documentation/assets/form-rendered.png new file mode 100644 index 0000000..7d37357 Binary files /dev/null and b/Documentation/assets/form-rendered.png differ diff --git a/Documentation/assets/npm-start.png b/Documentation/assets/npm-start.png new file mode 100644 index 0000000..91f3f56 Binary files /dev/null and b/Documentation/assets/npm-start.png differ diff --git a/Documentation/assets/php-server-config.png b/Documentation/assets/php-server-config.png new file mode 100644 index 0000000..cb886c8 Binary files /dev/null and b/Documentation/assets/php-server-config.png differ diff --git a/Documentation/assets/render-react-php-file-processed.png b/Documentation/assets/render-react-php-file-processed.png new file mode 100644 index 0000000..3b0c1eb Binary files /dev/null and b/Documentation/assets/render-react-php-file-processed.png differ diff --git a/Documentation/assets/staging-server-render-react-php-file-processed.png b/Documentation/assets/staging-server-render-react-php-file-processed.png new file mode 100644 index 0000000..08c3555 Binary files /dev/null and b/Documentation/assets/staging-server-render-react-php-file-processed.png differ diff --git a/Documentation/how-to-dev.md b/Documentation/how-to-dev.md new file mode 100644 index 0000000..046992f --- /dev/null +++ b/Documentation/how-to-dev.md @@ -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 ` +``` + +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( + + //here's our view component + + ); +} +``` + +## 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 ( +
+

username: {username}

//arguments are used heres +

password: {password}

+
+ ) +} +``` + +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( + + + + ); +} +``` +let's take a look at this line: +``` + +``` +`DisplayResults` is the name of our React Component (the function). +To pass arguments to the `DisplayResults` function, we use the syntax `={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)). \ No newline at end of file diff --git a/ci/.drone.yml b/ci/.drone.yml index 7d8f9a4..01f5ab5 100644 --- a/ci/.drone.yml +++ b/ci/.drone.yml @@ -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 diff --git a/config.php b/config.php index 111b11b..b2241d8 100644 --- a/config.php +++ b/config.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 diff --git a/profiles/prod-config-profile.php b/profiles/prod-config-profile.php index 5f4bd45..04d9529 100644 --- a/profiles/prod-config-profile.php +++ b/profiles/prod-config-profile.php @@ -1,9 +1,13 @@ setBasePath($basePath);