XF 2.2 Multiple XF::phrase() keys in one go?

asprin

Active member
One of my repositories contains the following lines of code:

PHP:
$title = \XF::phrase('asp_fb_title');
$desc = \XF::phrase('asp_fb_desc');
$extra = \XF::phrase('asp_fb_extra');
.
.
.
.

Upon inspecting the queries being run behind the scenes (via ?_debug=1), I realized that it was making 3 queries - one each for \XF::phrase() line

SQL:
SELECT title, phrase_text
FROM xf_phrase_compiled
WHERE language_id = ? AND title IN ('asp_fb_title')

SELECT title, phrase_text
FROM xf_phrase_compiled
WHERE language_id = ? AND title IN ('asp_fb_desc')

SELECT title, phrase_text
FROM xf_phrase_compiled
WHERE language_id = ? AND title IN ('asp_fb_extra')

So I was wondering if, given that the query is using an IN operator, there is a way to pull multiple phrases from a single query so that I cut down the number of queries made to show the intended page (from 3 to 1 in this case). Something like the below:

SQL:
SELECT title, phrase_text
FROM xf_phrase_compiled
WHERE language_id = ? AND title IN ('asp_fb_title', 'asp_fb_desc', 'asp_fb_extra')
 
Solution
I've used phrase groups to get around such a problem - renaming your phrase IDs like so should load them all at once as they'll be grouped together
  • asp_fb_title -> asp_fb.title
  • asp_fb_desc -> asp_fb.desc
  • asp_fb_extra -> asp_fb.extra
I've used phrase groups to get around such a problem - renaming your phrase IDs like so should load them all at once as they'll be grouped together
  • asp_fb_title -> asp_fb.title
  • asp_fb_desc -> asp_fb.desc
  • asp_fb_extra -> asp_fb.extra
 
Solution
Are your phrases created?

XenForo executes queries before rendering the phrase. The XF::phrase() method adds a phrase to the list of phrases to load, which will later be used to get phrases from the database when one of the phrases is rendered. After that, the phrases are added to the cache, so they will not be requested again:
SQL:
SELECT title, phrase_text
FROM xf_phrase_compiled
WHERE language_id = ?
    AND title IN ('existing_phrase_1', 'existing_phrase_2', 'existing_phrase_3', 'existing_phrase_4')
However, if the phrase does not exist, it can be queried from the database several times: the first time when some other phrase is rendered, and this one was added to the list for loading, and after that, when this phrase is rendered: it is not in the cache, so another query is executed:
SQL:
SELECT title, phrase_text
FROM xf_phrase_compiled
WHERE language_id = ?
    AND title IN ('existing_phrase', 'non_existing_phrase_1', 'non_existing_phrase_2', 'non_existing_phrase_3');

SELECT title, phrase_text
FROM xf_phrase_compiled
WHERE language_id = ?
    AND title IN ('non_existing_phrase_1');

SELECT title, phrase_text
FROM xf_phrase_compiled
WHERE language_id = ?
    AND title IN ('non_existing_phrase_2');

SELECT title, phrase_text
FROM xf_phrase_compiled
WHERE language_id = ?
    AND title IN ('non_existing_phrase_3');
 
If it's only a few phrases and they aren't rendered immediately: \XF::phraseDeferred()

If there are many phrases that are related and usually used together: Use a phrase group as already suggested by @apathy; phrase groups will not be loaded from DB at all but from aggragated PHP files in code-cache

If a handful phrases is required to be rendered immediately: \XF::language()->requirePhrases([''phrase1', 'phrase2', 'phrase3']); before using the first phrase.
 
I've used phrase groups to get around such a problem - renaming your phrase IDs like so should load them all at once as they'll be grouped together
  • asp_fb_title -> asp_fb.title
  • asp_fb_desc -> asp_fb.desc
  • asp_fb_extra -> asp_fb.extra
Interesting. So I would just need to call them as \XF::phrase('asp_fb.title'), \XF::phrase('asp_fb.desc'), \XF::phrase('asp_fb.extra')?

Are your phrases created?
Yes, they are present.

After that, the phrases are added to the cache, so they will not be requested again
But this doesn't seem to be the case in my instance. The number of queries is always +3 even after page refreshes. Could it be because of the dev mode being enabled?
 
Last edited:
But this doesn't seem to be the case in my instance. The number of queries is always +3 even after page refreshes. Could it be because of the dev mode being enabled?
It depends on how you use the phrases.

PHP:
// This will cause 3 additional queries (if none of the phrases is already cached)
$title = (string)\XF::phrase('asp_fb_title');
$desc = (string)\XF::phrase('asp_fb_desc');
$extra = (string)\XF::phrase('asp_fb_extra');
\XF::dump([$title, $desc, $extra]);

// This will cause 1  additional query (if at least one of the phrases is not already cached)
$title = \XF::phrase('asp_fb_title');
$desc = \XF::phrase('asp_fb_desc');
$extra = \XF::phrase('asp_fb_extra');
\XF::dump([(string)$title, (string)$desc, (string)$extra]);

// This will cause 3 additional queries (if none of the phreases is already cached)
$title = \XF::phraseDeferred('asp_fb_title');
$desc = \XF::phraseDeferred('asp_fb_desc');
$extra = \XF::phraseDeferred('asp_fb_extra');
\XF::dump([(string)$title, (string)$desc, (string)$extra]);

// This will cause 3 additional queries (if none of the phrases is already cached)
$title = (string)\XF::phraseDeferred('asp_fb_title');
$desc = (string)\XF::phraseDeferred('asp_fb_desc');
$extra = (string)\XF::phraseDeferred('asp_fb_extra');
\XF::dump([$title, $desc, $extra]);

// this will cause 1 additonal query (if at least one of the phrases is not already cached)
// It doesn't matter if you use phrase or phraseDeferred or when the phrase is rendered -
// it will always be max. one additional query
\XF::language()->requirePhrases(['asp_fb_title', 'asp_fb_desc', 'asp_fb_extra']);
$title = \XF::phrase('asp_fb_title');
$desc = \XF::phrase('asp_fb_desc');
$extra = \XF::phrase('asp_fb_extra');
\XF::dump([(string)$title, (string)$desc, (string)$extra]);
 
It depends on how you use the phrases.

PHP:
// This will cause 3 additional queries (if none of the phrases is already cached)
$title = (string)\XF::phrase('asp_fb_title');
$desc = (string)\XF::phrase('asp_fb_desc');
$extra = (string)\XF::phrase('asp_fb_extra');
\XF::dump([$title, $desc, $extra]);

// This will cause 1  additional query (if at least one of the phrases is not already cached)
$title = \XF::phrase('asp_fb_title');
$desc = \XF::phrase('asp_fb_desc');
$extra = \XF::phrase('asp_fb_extra');
\XF::dump([(string)$title, (string)$desc, (string)$extra]);

// This will cause 3 additional queries (if none of the phreases is already cached)
$title = \XF::phraseDeferred('asp_fb_title');
$desc = \XF::phraseDeferred('asp_fb_desc');
$extra = \XF::phraseDeferred('asp_fb_extra');
\XF::dump([(string)$title, (string)$desc, (string)$extra]);

// This will cause 3 additional queries (if none of the phrases is already cached)
$title = (string)\XF::phraseDeferred('asp_fb_title');
$desc = (string)\XF::phraseDeferred('asp_fb_desc');
$extra = (string)\XF::phraseDeferred('asp_fb_extra');
\XF::dump([$title, $desc, $extra]);

// this will cause 1 additonal query (if at least one of the phrases is not already cached)
// It doesn't matter if you use phrase or phraseDeferred or when the phrase is rendered -
// it will always be max. one additional query
\XF::language()->requirePhrases(['asp_fb_title', 'asp_fb_desc', 'asp_fb_extra']);
$title = \XF::phrase('asp_fb_title');
$desc = \XF::phrase('asp_fb_desc');
$extra = \XF::phrase('asp_fb_extra');
\XF::dump([(string)$title, (string)$desc, (string)$extra]);
This is very informative. Thanks.
 
Back
Top Bottom