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.
<!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>
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 casehandle_form.php
which is located in the same folder asindex.php
method
- how should this information be sent:POST
orGET
. Default isGET
<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)
<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
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 amethod
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
$_POST = [
"username" => "zero_cool",
"favorite_number" => "10"
];
The JavaScript equivalent would be:
var $_POST = {
username: "zero_cool",
favorite_number: "10"
};
We can send the same information with GET
instead:
<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
// Inside of `handle_form.php`
echo $_GET["username"];
echo $_GET["favorite_number"];
example
<?php
$_GET = [
"username" => "zero_cool",
"favorite_number" => "10"
];
The JavaScript equivalent would be:
var $_GET = {
username: "zero_cool",
favorite_number: "10"
};
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 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
.
<a href="http://localhost:8888/handle_form.php?username=zero_cool&favorite_number=10">
Click me to submit
</a>
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.
// 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, `addEventListener` 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
// 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, `addEventListener` 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
or multipart/form-data
which is what PHP is expecting to recieve. The easiest way to handle this is via the FormData
-object. We can also make it accept JSON but it's easier to use the FormData
-object.
// 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, `addEventListener` 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);
postData(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);
}
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:
form.addEventListener('click', function(event){
event.preventDefault();
const formattedFormData = new FormData(form);
formattedFormData.append('property', 'value');
postData(formattedFormData);
});
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
.
const form = document.getElementById('example_form');
form.addEventListener('click', function(event){
event.preventDefault();
const formattedFormData = new FormData(form);
postData(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
// Inside of `handle_form.php`
echo $_POST["username"];
echo $_POST["favorite_number"];
echo $_GET["name"];
echo $_GET["favorite_color"];
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:
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
}
postData(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
//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
//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:
const data = await response.text();
const data = await response.json();
This is useful when we are restricted by 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
We can make a small proxy-file that handles redirecting the data via our own server. Let's use Pokémon API 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:
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:
<?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.
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
Thanks for the amazing tutorial. Could you please also write 2 lines on error handling?