Skip to content

Instantly share code, notes, and snippets.

@landinialejandro
Forked from jesperorb/php_form_submit.md
Created May 19, 2022 23:21
Show Gist options
  • Save landinialejandro/84d17c94b71d2b0a0acdba43c4ccf44b to your computer and use it in GitHub Desktop.
Save landinialejandro/84d17c94b71d2b0a0acdba43c4ccf44b to your computer and use it in GitHub Desktop.

Revisions

  1. Jesper Orb revised this gist Oct 31, 2020. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions php_form_submit.md
    Original file line number Diff line number Diff line change
    @@ -251,7 +251,7 @@ form.addEventListener('click', function(event){
    event.preventDefault();
    const formattedFormData = new FormData(form);
    formattedFormData.append('property', 'value');
    fetchData(formattedFormData);
    postData(formattedFormData);
    });
    ```

    @@ -265,7 +265,7 @@ const form = document.getElementById('example_form');
    form.addEventListener('click', function(event){
    event.preventDefault();
    const formattedFormData = new FormData(form);
    fetchData(formattedFormData);
    postData(formattedFormData);
    });

    async function postData(formattedFormData){
    @@ -314,7 +314,7 @@ form.addEventListener('click', function(event){
    username: this.username.value,
    favorite_number: this.favorite_number.value
    }
    fetchData(formattedFormData);
    postData(formattedFormData);
    });

    async function postData(formattedFormData){
  2. Jesper Orb revised this gist Sep 19, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion php_form_submit.md
    Original file line number Diff line number Diff line change
    @@ -216,7 +216,7 @@ form.addEventListener('click', function(event){
    * This will create a body-object that PHP can read properly
    */
    const formattedFormData = new FormData(form);
    fetchData(formattedFormData);
    postData(formattedFormData);
    });

    async function postData(formattedFormData){
  3. Jesper Orb revised this gist Apr 4, 2018. 1 changed file with 14 additions and 10 deletions.
    24 changes: 14 additions & 10 deletions php_form_submit.md
    Original file line number Diff line number Diff line change
    @@ -39,7 +39,7 @@ Lets focus on the form. To successfully submit a form you need to have your inpu

    * `action` - telling the form to **which** file should this information be sent, in this case `handle_form.php` which is located in the same folder as `index.php`
    * `method` - **how** should this information be sent: `POST` or `GET`. Default is `GET`
    * `<input type="submit">` - To be able to send the form via click, even if you handle this via JavaScript, you should always ha a "Send"-button.
    * `<input type="submit">` - To be able to send the form via click, even if you handle this via JavaScript, you should always ha a "Send"-button. This button can also be `<button type="submit"> Send </button>` to allow HTML inside of the button (for example to add an icon inside a button)

    ```html
    <form action="handle_form.php" method="POST" id="example_form">
    @@ -63,6 +63,7 @@ echo $_POST["favorite_number"];

    * If we use `method="POST"` on the form the values will be stored in `$_POST`
    * If we use `method="GET"` or do not supply a `method` to the form the values will be stored in `$_GET`
    * PHP also has a `$_REQUEST`-variable that can contain these values.

    `$_POST` and `$_GET` will always be an _Associative Array_, this is how we would write the variable if it wouldn't get created for us:
    ```php
    @@ -129,7 +130,7 @@ The other difference is that the information will be available inside of the URL
    http://localhost:8888/handle_form.php?username=zero_cool&favorite_number=10
    ```

    This also means that we can basically call PHP-files like the one handling our form with a link instead. So if we where to write the code below, we would get the same result. This is only possible with `GET`-requests because the information is always sent via the URL and **not** inside of body like with `POST`.
    This also means that we can basically call PHP-files like the one handling our form with a link instead. So if we were to write the code below, we would get the same result. This is only possible with `GET`-requests because the information is always sent via the URL and **not** inside of body like with `POST`.

    ```html
    <a href="http://localhost:8888/handle_form.php?username=zero_cool&favorite_number=10">
    @@ -148,7 +149,7 @@ const form = document.getElementById('example_form');
    /**
    * Add an onclick-listener to the whole form, the callback-function
    * will always know what you have clicked and supply your function with
    * an event-object as first parameter, `addEvenListener` creates this for us
    * an event-object as first parameter, `addEventListener` creates this for us
    */
    form.addEventListener('click', function(event){
    //Prevent the event from submitting the form, no redirect or page reload
    @@ -165,7 +166,7 @@ const form = document.getElementById('example_form');
    /**
    * Add an onclick-listener to the whole form, the callback-function
    * will always know what you have clicked and supply your function with
    * an event-object as first parameter, `addEvenListener` creates this for us
    * an event-object as first parameter, `addEventListener` creates this for us
    */
    form.addEventListener('click', function(event){
    //Prevent the event from submitting the form, no redirect or page reload
    @@ -204,7 +205,7 @@ const form = document.getElementById('example_form');
    /**
    * Add an onclick-listener to the whole form, the callback-function
    * will always know what you have clicked and supply your function with
    * an event-object as first parameter, `addEvenListener` creates this for us
    * an event-object as first parameter, `addEventListener` creates this for us
    */
    form.addEventListener('click', function(event){
    //Prevent the event from submitting the form, no redirect or page reload
    @@ -215,7 +216,7 @@ form.addEventListener('click', function(event){
    * This will create a body-object that PHP can read properly
    */
    const formattedFormData = new FormData(form);
    postData(formattedFormData);
    fetchData(formattedFormData);
    });

    async function postData(formattedFormData){
    @@ -250,7 +251,7 @@ form.addEventListener('click', function(event){
    event.preventDefault();
    const formattedFormData = new FormData(form);
    formattedFormData.append('property', 'value');
    postData(formattedFormData);
    fetchData(formattedFormData);
    });
    ```

    @@ -264,7 +265,7 @@ const form = document.getElementById('example_form');
    form.addEventListener('click', function(event){
    event.preventDefault();
    const formattedFormData = new FormData(form);
    postData(formattedFormData);
    fetchData(formattedFormData);
    });

    async function postData(formattedFormData){
    @@ -313,7 +314,7 @@ form.addEventListener('click', function(event){
    username: this.username.value,
    favorite_number: this.favorite_number.value
    }
    postData(formattedFormData);
    fetchData(formattedFormData);
    });

    async function postData(formattedFormData){
    @@ -396,4 +397,7 @@ $response = file_get_contents('http://pokeapi.co/api/v2/pokemon/' . $_GET["pokem
    echo $response;
    ```

    Notice that we are sending along which Pokémon to be fetched by adding a query parameter after `?`. The key will be `$_GET["pokemon"]` and the value will be `25` in this case. No we will not get any CORS errors.
    Notice that we are sending along which Pokémon to be fetched by adding a query parameter after `?`. The key will be `$_GET["pokemon"]` and the value will be `25` in this case. No we will not get any CORS errors.

    **Be cautious and do not trust data from the user. Sending data from the user with a request like this can be harmful. Always validate your input before sending it**

  4. Jesper Orb revised this gist Apr 4, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion php_form_submit.md
    Original file line number Diff line number Diff line change
    @@ -390,7 +390,7 @@ async function fetchPokemon(){

    Inside `fetch_pokemon.php` we use `file_get_contents` that can usually be used to fetch data from an API:

    ```js
    ```php
    <?php
    $response = file_get_contents('http://pokeapi.co/api/v2/pokemon/' . $_GET["pokemon"]);
    echo $response;
  5. Jesper Orb revised this gist Apr 4, 2018. 1 changed file with 23 additions and 0 deletions.
    23 changes: 23 additions & 0 deletions php_form_submit.md
    Original file line number Diff line number Diff line change
    @@ -374,3 +374,26 @@ const data = await response.text();
    const data = await response.json();
    ```

    ## PHP API Proxy

    This is useful when we are restricted by **[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)** which means that some APIs do not allow us to make requests to their API via a browser. I have written a small guide to getting around that problem here: **[Handle CORS Client-side](https://gist.github.com/jesperorb/6ca596217c8dfba237744966c2b5ab1e)**

    We can make a small proxy-file that handles redirecting the data via our own server. Let's use **[Pokémon API](https://pokeapi.co/)** as an example because the do not allow CORS. So instead of calling the API directly we call our own file that calls the API:

    ```js
    async function fetchPokemon(){
    const response = await fetch('fetch_pokemon.php?pokemon=25');
    const pokemon = await response.json();
    console.log(pokemon);
    }
    ```

    Inside `fetch_pokemon.php` we use `file_get_contents` that can usually be used to fetch data from an API:

    ```js
    <?php
    $response = file_get_contents('http://pokeapi.co/api/v2/pokemon/' . $_GET["pokemon"]);
    echo $response;
    ```
    Notice that we are sending along which Pokémon to be fetched by adding a query parameter after `?`. The key will be `$_GET["pokemon"]` and the value will be `25` in this case. No we will not get any CORS errors.
  6. Jesper Orb revised this gist Apr 4, 2018. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions php_form_submit.md
    Original file line number Diff line number Diff line change
    @@ -215,7 +215,7 @@ form.addEventListener('click', function(event){
    * This will create a body-object that PHP can read properly
    */
    const formattedFormData = new FormData(form);
    fetchData(formattedFormData);
    postData(formattedFormData);
    });

    async function postData(formattedFormData){
    @@ -250,7 +250,7 @@ form.addEventListener('click', function(event){
    event.preventDefault();
    const formattedFormData = new FormData(form);
    formattedFormData.append('property', 'value');
    fetchData(formattedFormData);
    postData(formattedFormData);
    });
    ```

    @@ -264,7 +264,7 @@ const form = document.getElementById('example_form');
    form.addEventListener('click', function(event){
    event.preventDefault();
    const formattedFormData = new FormData(form);
    fetchData(formattedFormData);
    postData(formattedFormData);
    });

    async function postData(formattedFormData){
    @@ -313,7 +313,7 @@ form.addEventListener('click', function(event){
    username: this.username.value,
    favorite_number: this.favorite_number.value
    }
    fetchData(formattedFormData);
    postData(formattedFormData);
    });

    async function postData(formattedFormData){
  7. Jesper Orb renamed this gist Apr 4, 2018. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  8. Jesper Orb created this gist Apr 4, 2018.
    376 changes: 376 additions & 0 deletions php_form_submit.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,376 @@
    # PHP Form submitting

    If we have the following structure in our application:

    * 📁 application_folder_name
    - 📄 index.php
    - 📄 handle_form.php
    - 📄 main.js

    And we fill our `index.php` with the following content just to get a basic website with a form working. You should be able to run this through a php-server of your choice.

    ```html
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>PHP</title>
    </head>
    <body>

    <form action="handle_form.php" method="POST" id="example_form">
    <label for="username">Username</label>
    <input type="text" name="username" id="username">
    <label for="favorite_number">Favorite number</label>
    <input type="number" name="favorite_number" id="favorite_number">
    <input type="submit" value="Skicka">
    </form>

    <script src="main.js"></script>
    </body>
    </html>
    ```

    ### Submitting a form with PHP

    Lets focus on the form. To successfully submit a form you need to have your inputs inside of the `form`-element, this indicates that the inputs shall be sent along with the form if we submit the form "like normal". To send the form without JavaScript all inputs need to have a `name`, in fact they should have a `name` regardless if you are sending the data via JavaScript or PHP. The actual form needs **3** things:

    * `action` - telling the form to **which** file should this information be sent, in this case `handle_form.php` which is located in the same folder as `index.php`
    * `method` - **how** should this information be sent: `POST` or `GET`. Default is `GET`
    * `<input type="submit">` - To be able to send the form via click, even if you handle this via JavaScript, you should always ha a "Send"-button.

    ```html
    <form action="handle_form.php" method="POST" id="example_form">
    <label for="username">Username</label>
    <input type="text" name="username" id="username">
    <label for="favorite_number">Favorite number</label>
    <input type="number" name="favorite_number" id="favorite_number">
    <input type="submit" value="Skicka">
    </form>
    ```

    All this information needs to exists in HTML to successfully send a request. If this markup is correct we should be able to submit the form to the file `handle_form.php`:

    ```php
    <?php
    echo $_POST["username"];
    echo $_POST["favorite_number"];
    ```
    `<input name="username">` will result in the value of the input field being stored in `$_POST["username"]`. So the `name` of the input field will decide what the variable is called in PHP.

    * If we use `method="POST"` on the form the values will be stored in `$_POST`
    * If we use `method="GET"` or do not supply a `method` to the form the values will be stored in `$_GET`

    `$_POST` and `$_GET` will always be an _Associative Array_, this is how we would write the variable if it wouldn't get created for us:
    ```php
    <?php
    $_POST = [
    "username" => "zero_cool",
    "favorite_number" => "10"
    ];
    ```

    The JavaScript equivalent would be:

    ```js
    var $_POST = {
    username: "zero_cool",
    favorite_number: "10"
    };
    ```

    #### Using `GET` instead of `$_POST`

    We can send the same information with `GET` instead:

    ```html
    <form action="handle_form.php" method="GET" id="example_form">
    <label for="username">Username</label>
    <input type="text" name="username" id="username">
    <label for="favorite_number">Favorite number</label>
    <input type="number" name="favorite_number" id="favorite_number">
    <input type="submit" value="Skicka">
    </form>
    ```

    The only difference in this case is that we store the data inside of `$_GET` instead of `$_POST`.

    ```php
    <?php
    // Inside of `handle_form.php`
    echo $_GET["username"];
    echo $_GET["favorite_number"];
    ```
    _example_
    ```php
    <?php
    $_GET = [
    "username" => "zero_cool",
    "favorite_number" => "10"
    ];
    ```
    The JavaScript equivalent would be:
    ```js
    var $_GET = {
    username: "zero_cool",
    favorite_number: "10"
    };
    ```
    The other difference is that the information will be available inside of the URL:
    ```http
    http://localhost:8888/handle_form.php?username=zero_cool&favorite_number=10
    ```
    This also means that we can basically call PHP-files like the one handling our form with a link instead. So if we where to write the code below, we would get the same result. This is only possible with `GET`-requests because the information is always sent via the URL and **not** inside of body like with `POST`.

    ```html
    <a href="http://localhost:8888/handle_form.php?username=zero_cool&favorite_number=10">
    Click me to submit
    </a>
    ```

    ### Submitting a form with JavaScript

    We can submit the same form with the use of JavaScript without reloading the page. The same logic will be applied inside of the PHP-file and most of the work must be done in JavaScript. If we have the same form as above we must first make sure the form **isn't** being submitted. A forms default behavior is to redirect you to a new site, which is fine if we don't have JavaScript. But in this case we can use JavaScript to our advantage.

    ```js
    // Get the whole form, not the individual input-fields
    const form = document.getElementById('example_form');
    /**
    * Add an onclick-listener to the whole form, the callback-function
    * will always know what you have clicked and supply your function with
    * an event-object as first parameter, `addEvenListener` creates this for us
    */
    form.addEventListener('click', function(event){
    //Prevent the event from submitting the form, no redirect or page reload
    event.preventDefault();
    });
    ```
    No we have stopped the form from behaving like it normally would. That also means that we need to specify what it should do instead. So we know that we need to communicate with the backend. Even though the client side and the server side are on the same domain, JavaScript can't communicate back to PHP without going through AJAX. AJAX must always be used. Let's use the native built in way of using AJAX: `fetch`. Notice that the first code below will not work, it is just a first step

    ```js
    // Get the whole form, not the individual input-fields
    const form = document.getElementById('example_form');
    /**
    * Add an onclick-listener to the whole form, the callback-function
    * will always know what you have clicked and supply your function with
    * an event-object as first parameter, `addEvenListener` creates this for us
    */
    form.addEventListener('click', function(event){
    //Prevent the event from submitting the form, no redirect or page reload
    event.preventDefault();
    postData();
    });

    async function postData(){
    /*
    * We are still using the same file as before and we are still not touching
    * either the backend code or the actual form. We could grab
    * the action-attribute from the form but it's easier to just put
    * in the 'URL' here. We don't need to supply PORT or 'localhost'
    */
    const response = await fetch('handle_form.php');
    /*
    * Because we are using `echo` inside of `handle_form.php` the response
    * will be a string and not JSON-data. Because of this we need to use
    * `response.text()` instead of `response.json()` to convert it to someting
    * that JavaScript understands
    */
    const data = await response.text();
    //This should later print out the values submitted through the form
    console.log(data);
    }
    ```
    But we are still missing the information to be sent. In raw PHP/HTML the form handles converting the data and sending it to `handle_form.php`. If we write our own logic for sending the data we must also make sure that the data is properly formatted.
    The data needs to be formatted to [`x-www-form-urlencoded`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) or [`multipart/form-data`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) which is what PHP is expecting to recieve. The easiest way to handle this is via the [`FormData`-object](https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects). We can also make it accept JSON but it's easier to use the `FormData`-object.

    ```js
    // Get the whole form, not the individual input-fields
    const form = document.getElementById('example_form');

    /**
    * Add an onclick-listener to the whole form, the callback-function
    * will always know what you have clicked and supply your function with
    * an event-object as first parameter, `addEvenListener` creates this for us
    */
    form.addEventListener('click', function(event){
    //Prevent the event from submitting the form, no redirect or page reload
    event.preventDefault();
    /**
    * If we want to use every input-value inside of the form we can call
    * `new FormData()` with the form we are submitting as an argument
    * This will create a body-object that PHP can read properly
    */
    const formattedFormData = new FormData(form);
    fetchData(formattedFormData);
    });

    async function postData(formattedFormData){
    /**
    * If we want to 'POST' something we need to change the `method` to 'POST'
    * 'POST' also expectes the request to send along values inside of `body`
    * so we must specify that property too. We use the earlier created
    * FormData()-object and just pass it along.
    */
    const response = await fetch('handle_form.php',{
    method: 'POST',
    body: formattedFormData
    });
    /*
    * Because we are using `echo` inside of `handle_form.php` the response
    * will be a string and not JSON-data. Because of this we need to use
    * `response.text()` instead of `response.json()` to convert it to someting
    * that JavaScript understands
    */
    const data = await response.text();
    //This should now print out the values that we sent to the backend-side
    console.log(data);
    }
    ```

    #### Adding more values to `FormData` in JavaScript

    If we want to add stuff that isn't inside of the form to this object we can use the `.append()`-method on the `FormData`-object:

    ```js
    form.addEventListener('click', function(event){
    event.preventDefault();
    const formattedFormData = new FormData(form);
    formattedFormData.append('property', 'value');
    fetchData(formattedFormData);
    });
    ```


    #### Sending both `GET` AND `POST`

    The form and the `fetch`-call will only either use `GET` or `POST`. A request can only be one of these requests. We can however send along values in the URL and these will be stored inside of the `$_GET`-variable even though our request is a `POST`.

    ```js
    const form = document.getElementById('example_form');
    form.addEventListener('click', function(event){
    event.preventDefault();
    const formattedFormData = new FormData(form);
    fetchData(formattedFormData);
    });

    async function postData(formattedFormData){
    /**
    * The request is still 'POST' but the $_GET variable
    * will get values too: 'name' and 'favorite_color'
    */
    const response = await fetch(
    'handle_form.php?name=Jesper&favorite_color=pink',
    {
    method: 'POST',
    body: formattedFormData
    }
    );
    const data = await response.text();
    console.log(data);
    }
    ```

    This would result in the following variables being set inside of `handle_form.php`:

    ```php
    <?php
    // Inside of `handle_form.php`
    echo $_POST["username"];
    echo $_POST["favorite_number"];
    echo $_GET["name"];
    echo $_GET["favorite_color"];
    ```

    #### Using `JSON`
    `FormData` is used when the server expects to recieve data in the form of `x-www-form-urlencoded`. This isn't always the case, some servers prefer that you instead send the data as a `JSON`-object. This means that we have to make some changes to both the frontend and the backend. If we use the same form as earlier:

    ```js
    const form = document.getElementById('example_form');
    form.addEventListener('click', function(event){
    event.preventDefault();
    /*
    * There is no shortcut like with 'new FormData(this)', we need
    * to construct the form-object ourselves. We are creating a regular
    * object instead of a FormData-object. `this` refers to the form,
    * we could also write: form.username.value and form.favorite_number.value
    */
    const formattedFormData = {
    username: this.username.value,
    favorite_number: this.favorite_number.value
    }
    fetchData(formattedFormData);
    });

    async function postData(formattedFormData){
    /**
    * The request is still 'POST' but the $_GET variable
    * will get values too: 'name' and 'favorite_color'
    */
    const response = await fetch(
    'handle_form.php',
    {
    method: 'POST',
    /*
    * We also need to stringify the values, turn the
    * JavaScript object to a single string that is accepted
    * as JSON. So we are sending one string that contains
    * all our values
    */
    body: JSON.stringify(formattedFormData)
    }
    );
    const data = await response.text();
    console.log(data);
    }
    ```

    If we choose to send the data like this we also need to tell PHP to expect the data to be accepted like this:

    ```php
    <?php
    //inside of 'handle_form.php'

    /* We are parsing all the data that is being sent as JSON. `json_decode`
    * turn our JSON-object into a PHP Associative array and stores it inside of
    * `$data`
    */
    $data = json_decode(file_get_contents('php://input'), true);
    echo $data["username"];
    echo $data["favorite_number"];
    ```

    If we choose to accept data as JSON we should probably also send back JSON. So if we want to '`echo`' back some value in the form of json, we should use `json_encode` before sending it. This is a bit dumb example tho because we are not doing anything with the data, but it's more to get a hang of the syntax and how it works.

    ```php
    <?php
    //inside of 'handle_form.php'
    $data = json_decode(file_get_contents('php://input'), true);

    echo json_encode($data);
    ```

    If we return JSON we must also use `.json()` instead of `.text()` in JavaScript:

    ```js
    const data = await response.text();
    ```
    ```js
    const data = await response.json();
    ```