Guides

Handling Ajax post requests with Symfony

Handling PHP forms and accessing the data that follows user input is the bread and butter of learning PHP. Symfony adds a Model-View-Controller (MVC) architecture with reusable components, a well-organized structure, and best practices to the development process, making it a typical environment for handling user input. Ajax, the technology that enables dynamic content on web pages, allows users to complete actions on the page without page transitions or reloads by processing data in the background, sending requests to the server and updating the page view according to the responses. Also known as asynchronous Javascript, Ajax functionality is often used instead of PHP forms to send user input data to the server.

Challenge: Adding items to shopping cart

Let’s assume we want to add items to the shopping cart. Resulting from the user input, we have a set of items with an id and quantity. The (id, quantity) pairs are the data that we send asynchronously to the server. Our choice of technology is a commonly used Javascript library called axios which comes with an interface for sending post requests to a given URL with the data in the request body. The reason behind that choice is that it’s easy to integrate into a VueJS or ReactJS environment.

The function call

axios.post(url, payload)

sends an asynchronous HTTP POST request to the route of the url variable with the data stored in the payload variable.

The Symfony controller handling the route is called addItems.

use Symfony\Component\HttpFoundation\Request;
public function addItems (Request $request) {

    // process data here, for example:
    $quantity = $request->request->get('quantity');

}

Normally, when processing PHP form data, the form field values are accessible through the $_POST variable which is mapped to the request. When we post an HTML form, the data is accessible as expected, but now that we make an Ajax request with axios, the request is empty. Did our data disappear? Why? Let’s compare HTML form submissions and axios ajax requests.

Cause: content type differences

Posting an HTML form sends the payload as a url-encoded string by default. One of the request headers indicates the content type.

Content-Type: application/x-www-form-urlencoded

Axios.post serializes the payload into a JSON string by default which also shows in the content type header.

Content-Type: application/json;

Because the JSON format allows for the storage of data with an arbitrary depth, it cannot be safely mapped to the Parameter bag PHP structure of $request->request. So we have to adjust our code to work with axios.post requests.

Code that solves the problem

Solution #1

Send the payload as JSON and read Symfony’s normalized request payload. In current Symfony versions, getPayload() provides a consistent parameter bag for JSON and regular form data.

$quantity = $request->getPayload()->get('quantity');

Solution #2

Send the payload as a url-encoded string by modifying the Javascript code. We create a URLSearchParams object for the payload because axios does not automatically convert it into a JSON string. In a browser environment, we may also use the FormData interface instead of URLSearchParams.

const params = new URLSearchParams();
params.append('id',1234567890);
params.append('quantity',4);

Then post the data. For example:

const submitData = () => {
  try {
    return axios.post(url,params);
  } catch (error) {
    console.log(error);
  }
}
response = submitData()
  .then(({data}) => {
    console.log(data);
  });

Then the post data is normally accessible in the controller, for example :

$quantity = $request->request->get('quantity');

Other solutions

Whenever the first idea feels too hard or time-consuming to implement, we are inclined to look for an alternative solution. So how about sending an HTTP GET request instead? Packing the few parameters in the URL should be ok, right? It’s easy, it works, so why not use axios.get? Well here’s why not.

Something being easy or quick to implement is not relevant when we choose between HTTP post and get requests. Instead, we should ask if we can repeat the action without consequences, a feature also known as idempotence. For example, adding items to the shopping cart should always have consequences which show as data updates on the server. Each repeat of the action should add more items to the cart. So what if we implement this with a “get” request? The first time probably works just fine but the second time – we may get an “items successfully added” response message cached by the browser with no request ever reaching the server. Besides being a clearly unwanted consequence, it also explains why a get request cannot replace a post request.

Frequently Asked Questions

Why is $request->request empty after an Axios POST to Symfony?

Axios commonly sends an object as JSON with Content-Type: application/json. Traditional form fields populate Symfony’s form parameter bag, but JSON has to be read from the request payload. In current Symfony versions, use $request->getPayload() or map the JSON body to a DTO.

How do I read a JSON POST body in a Symfony controller?

For scalar values, $request->getPayload()->get('field') reads the normalized payload. For a structured API request, Symfony can map and validate JSON directly into a typed DTO with the #[MapRequestPayload] attribute.

Should Axios send JSON or URL-encoded form data to Symfony?

Use JSON for an API-style endpoint and declare the expected request format clearly. Use URL-encoded data or FormData when the endpoint is designed like a traditional form submission. The client content type and the server-side parsing strategy must agree.

Can an HTTP GET request replace a POST request?

No when the operation changes server state. GET should be safe and repeatable without side effects, while POST is appropriate for actions such as adding a cart item or creating a record. Using GET for mutations can introduce caching, security and semantic problems.

Contact us

Let’s find the right solution for your business.

We partner with product teams and founders to design, build, and ship software that lasts.

Talk to our team