8000 Merge branch '6.3' into 6.4 · symfony/symfony-docs@b686913 · GitHub
[go: up one dir, main page]

Skip to content

Commit b686913

Browse files
committed
Merge branch '6.3' into 6.4
* 6.3: [HttpFoundation] Add documentation for `StreamedJsonResponse`
2 parents 88aed87 + 3be5f6a commit b686913

File tree

1 file changed

+81
-0
lines changed

1 file changed

+81
-0
lines changed

components/http_foundation.rst

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,86 @@ represented by a PHP callable instead of a string::
629629
// disables FastCGI buffering in nginx only for this response
630630
$response->headers->set('X-Accel-Buffering', 'no');
631631

632+
Streaming a JSON Response
633+
~~~~~~~~~~~~~~~~~~~~~~~~~
634+
635+
.. versionadded:: 6.3
636+
637+
The :class:`Symfony\\Component\\HttpFoundation\\StreamedJsonResponse` class was
638+
introduced in Symfony 6.3.
639+
640+
The :class:`Symfony\\Component\\HttpFoundation\\StreamedJsonResponse` allows to
641+
stream large JSON responses using PHP generators to keep the used resources low.
642+
643+
The class constructor expects an array which represents the JSON structure and
644+
includes the list of contents to stream. In addition to PHP generators, which are
645+
recommended to minimize memory usage, it also supports any kind of PHP Traversable
646+
containing JSON serializable data::
647+
648+
use Symfony\Component\HttpFoundation\StreamedJsonResponse;
649+
650+
// any method or function returning a PHP Generator
651+
function loadArticles(): \Generator {
652+
yield ['title' => 'Article 1'];
653+
yield ['title' => 'Article 2'];
654+
yield ['title' => 'Article 3'];
655+
};
656+
657+
$response = new StreamedJsonResponse(
658+
// JSON structure with generators in which will be streamed as a list
659+
[
660+
'_embedded' => [
661+
'articles' => loadArticles(),
662+
],
663+
],
664+
);
665+
666+
When loading data via Doctrine, you can use the ``toIterable()`` method to
667+
fetch results row by row and minimize resources consumption.
668+
See the `Doctrine Batch processing`_ documentation for more::
669+
670+
public function __invoke(): Response
671+
{
672+
return new StreamedJsonResponse(
673+
[
674+
'_embedded' => [
675+
'articles' => $this->loadArticles(),
676+
],
677+
],
678+
);
679+
}
680+
681+
public function loadArticles(): \Generator
682+
{
683+
// get the $entityManager somehow (e.g. via constructor injection)
684+
$entityManager = ...
685+
686+
$queryBuilder = $entityManager->createQueryBuilder();
687+
$queryBuilder->from(Article::class, 'article');
688+
$queryBuilder->select('article.id')
689+
->addSelect('article.title')
690+
->addSelect('article.description');
691+
692+
return $queryBuilder->getQuery()->toIterable();
693+
}
694+
695+
If you return a lot of data, consider calling the :phpfunction:`flush` function
696+
after some specific item count to send the contents to the browser::
697+
698+
public function loadArticles(): \Generator
699+
{
700+
// ...
701+
702+
$count = 0;
703+
foreach ($queryBuilder->getQuery()->toIterable() as $article) {
704+
yield $article;
705+
706+
if (0 === ++$count % 100) {
707+
flush();
708+
}
709+
}
710+
}
711+
632712
.. _component-http-foundation-serving-files:
633713

634714
Serving Files
@@ -866,3 +946,4 @@ Learn More
866946
.. _`JSON Hijacking`: https://haacked.com/archive/2009/06/25/json-hijacking.aspx/
867947
.. _OWASP guidelines: https://cheatsheetseries.owasp.org/cheatsheets/AJAX_Security_Cheat_Sheet.html#always-return-json-with-an-object-on-the-outside
868948
.. _RFC 8674: https://tools.ietf.org/html/rfc8674
949+
.. _Doctrine Batch processing: https://www.doctrine-project.org/projects/doctrine-orm/en/2.14/reference/batch-processing.html#iterating-results

0 commit comments

Comments
 (0)
0