In this example we are going to return response to twig template to display. However, the response won't be a single one. There will be more than one responses and we will display them as soon as they arrive. For that we will be using StreamedResponse class of symfony.


Controller


namespace Football\FrontendBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

/**
* @Route("", service="football_frontend.controller.default")
*/
class DefaultController
{
private $templating;

public function __construct(
EngineInterface $templating
) {
$this->templating = $templating;
}

/**
* @Method({"GET"})
* @Route("")
*
* @return Response
*/
public function indexAction()
{
return $this->templating->renderResponse('FootballFrontendBundle:Default:index.html.twig');
}

/**
* @param Request $request
*
* @Method({"GET"})
* @Route("/ajax", name="default_ajax")
*
* @return Response
*/
public function ajaxAction(Request $request)
{
if (!$request->isXmlHttpRequest()) {
throw new BadRequestHttpException('AJAX request expected.');
}

$response = new StreamedResponse();
$response->setCallback(function () {
$repeat = 4;
for ($i = 1; $i < $repeat; ++$i) {
echo 'Line '.$i;

ob_flush();
flush();

if ($i < ($repeat - 1)) {
sleep(2);
}
}
});

return $response;
}
}

services:
football_frontend.controller.default:
class: Football\FrontendBundle\Controller\DefaultController
arguments:
- '@templating'

Twig template


{% extends '::base.html.twig' %}

{% block body %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
$(document).ready(function () {
$('button').click(function () {
var lastResponseLength = false;

$('#stream-started').show();
$('#stream-output').empty();
$('#stream-output').show();

setTimeout(startStreaming(lastResponseLength), 1500);
});

function startStreaming(lastResponseLength) {
xhr = new XMLHttpRequest();
xhr.open('GET', '{{ (path('default_ajax')) }}', true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

xhr.onprogress = function(e) {
var response = e.currentTarget.response;
var output = lastResponseLength === false
? response
: response.substring(lastResponseLength);

lastResponseLength = response.length;

$('#stream-output').append('<p>'+output+'</p>');
};

xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
$('#stream-output').append('<p>Completed!</p>');
}
};

xhr.send();
}
});
</script>

<h3>StreamedResponse!</h3>
<hr />
<button id="test">Start streaming</button>
<p id="stream-started" style="display: none;">Here comes the stream ...</p>
<code id="stream-output" style="display: none;"></code>
<hr />
{% endblock %}

Test


Go to you application home URL and click "Start streaming" button. You'll see response appearing one by one rather than all in one go. Image below is just for demonstration purposes so you'll just see only one each time new response arrives.