Use PHP value in multiple blocks
- Crell
- Member | 9
I have an expensive-to-compute value that I want used across multiple blocks. It's not a string value (that's covered by https://forum.nette.org/…tiple-places), but a PHP object. In particular, it's for pagination, and I want to put prev/next/first/last links both in the body and in the HTML header.
Something along the lines of:
{block links}
{var $pageNum = $query['page'] ?? 1}
{var $pageSize = 2}
{var $pagination = $pageTree->expensive(...)}
<link rel="first" href="{pageUrl($currentPage)}"/>
<link rel="last" href="{pageUrl($currentPage, ['page' => $pagination->lastPageNum()])}"/>
<link n:if="$pageNum < $pagination->lastPageNum()" rel="next" href="{pageUrl($currentPage, ['page' => $pageNum + 1])}"/>
<link n:if="$pageNum > 1" rel="previous" href="{pageUrl($currentPage, ['page' => $pageNum - 1])}"/>
{/block}
{block content}
{var $pageNum = $query['page'] ?? 1}
{var $pagination = $pageTree->expensive(...)}
{ * ... *}
<div>Showing page {$pagination->pageNum} of {$pagination->pageCount}</div>
<ul class="paginator" n:foreach="range(1, $pagination->pageCount) as $i">
<li><a href="?page={$i}">{$i}</a></li>
</ul>
{/block}
I've not figured out how to avoid the duplicate calls to
$pageTree->expensive()
, though. Any pointers?
(I'm sure someone is going to say “well pass it in from the PHP code,” but for my purposes that won't work. The $pageTree object is passed in, but I want to be able to call methods on it once and reuse the result.)
Last edited by Crell (2025-02-10 17:28)
- mskocik
- Member | 74
I needed something similar and I solved it this way: I passed simple
stdClass
object into template and I set it's prop when needed to
be able to read it later (It had some default value set). Because it was
parameter of the template it was available globally in whole blocks of the
template.
Or you can cache it directly inside $pageTree
. Cache result in
first run of expensive()
and return cached result on subsequent
calls. If that's undesired you can provide some wrapper class which will cache
result of $pageTree->expensive()
.
- nightfish
- Member | 526
@Crell
You will need to somehow cache the return value of
$pageTree->expensive(...)
. If I couldn't cache the value prior
to rendering the template, I would go with custom Latte function that does the
caching:
$cache = [];
$latte->addFunction('getPagination', function(PageTree $pageTree, int $pageNum, int $pageSize) use (&$cache): Pagination {
$cacheKey = spl_object_hash($pageTree) . '-' . $pageNum . '-' . $pageSize;
if (\array_key_exists($cacheKey, $cache) === false) {
$cache[$cacheKey] = $pageTree->expensive($pageNum, $pageSize);
}
return $cache[$cacheKey];
});
{block links}
{var $pageNum = $query['page'] ?? 1}
{var $pageSize = 2}
{var $pagination = getPagination($pageTree, $pageNum, $pageSize)}
...
{/block}
{block content}
{var $pageNum = $query['page'] ?? 1}
{var $pageSize = 2}
{var $pagination = getPagination($pageTree, $pageNum, $pageSize)}
...
{/block}
- Crell
- Member | 9
Hm! Just from a usability perspective, I like the idea of wrapping $pageTree into a template function rather than injecting it. That feels cleaner, regardless of the current issue. The $pageTree service can then be injected into the extension rather than the template.
Would it be possible then to internalize the $pageNum handling? I'd love to be able to move that away from the template author, at least in the common case. (The $query is the HTTP query of the request, passed through, mainly for doing things like this.) Something like accessing all values passed to the template from the function rather than just the ones passed. (That may mean I need a tag or something instead, not sure.)
In practice there's almost a dozen parameters to expensive(). A mix of strings, ints, bools, arrays, and DateTimeImmutables. That is definitely memoizable, but could be a bit clunky.
@mskocik's idea of essentially a hidden template-global variable is interesting. I will have to experiment with that. If I can make it “feel” nice to use that may be the solution to the problem at hand, even if I still switch to a function for pageTree.
Like, if I define my own tag, could I do something like this (making up syntax):
{dataset $pagination pageSearch(pageSize: 5, hidden: false, tags: ['a', 'b'])}
{block links}
<link rel="first" href="{pageUrl($currentPage)}"/>
<link rel="last" href="{pageUrl($currentPage, ['page' => $pagination->lastPageNum()])}"/>
<link n:if="$pageNum < $pagination->lastPageNum()" rel="next" href="{pageUrl($currentPage, ['page' => $pagination->nextPage()])}"/>
<link n:if="$pageNum > 1" rel="previous" href="{pageUrl($currentPage, $pagination->prevPage())}"/>
{var $pageNum = $query['page'] ?? 1}
{var $pageSize = 2}
{var $pagination = getPagination($pageTree, $pageNum, $pageSize)}
...
{/block}
{block content}
{foreach $pagination as $page}
// ...
{/foreach}
...
{/block}
With the $pagination variable stored inside the extension class somewhere. (I'm sure the above isn't right, but hopefully conveys what I'm thinking of.)