Reputation: 1017
I'm developing a multilingual website PHP and would like to get content in a given language if available and in another one if not. I will try to explain my problem below. If something is not clear, please let me know.
content
: content_id
, url
, date
content_l10n
: content_id
, l10n_id
, title
, description
l10n
: l10n_id
, name
, order
→ Easy to implement with a JOIN
.
→ How to implement that ? Is it possible in a single request ?
content
rows (for a list of titles, by example).title
is translated but description
is NULL
. Ideally, the request would select the title in the given language but would fallback to another language for the description. l10n
ordered by order
ASC).Your help will be really appreciated ! Thank you in advance !
Kind regards,
olivier
Upvotes: 3
Views: 2006
Reputation: 11581
This solution works for many rows but you need 1 LEFT JOIN per language, and the order of the JOIns specifies priority.
SELECT c.url, c.date,
COALESCE( c1.title, c2.title ),
COALESCE( c1.description, c2.description )
FROM content c
LEFT JOIN content_l10n c1 ON (c1.content_id = c.content_id AND c1.l10n_id=$1)
LEFT JOIN content_l10n c2 ON (c2.content_id = c.content_id AND c2.l10n_id=$2)
(Note : I suppose here that $1 and $2 are the user's first 2 preferred languages, and they are cached in the session, so there is no need for extra JOINs with l10n).
With your table structure this is the only meaningful way to set the language order. You would need an extra table to specify each user's language preferences instead of storing the order in l10n table. So let's suppose you have a table
user_l10n( user_id, l10n_id, order )
And let's suppose the table l10n keeps its "order" field, as a default.
If you do this :
SELECT ..., COALESCE(ul.order,l.order) AS order
FROM
content c
JOIN content_l10n cl USING (content_id)
JOIN l10n l USING (l10n_id) -- get default language order
LEFT JOIN user_l10n ul ON (ul.l10n_id=l.l10n_id -- get user preferences if available
AND ul.user_id=$user_id)
WHERE search condition on content, etc
ORDER BY content_id, COALESCE(ul.order,l.order)
You'll get all the documents that match, and also the user-specified (or default) ordering, so the application can easily sort this out.
Now the idea is to avoid retrieving from the database all the rows that are in languages that are "shadowed" by an existing translation in a language that the user prefers.
The natural way to do this is a GROUP BY, but MySQL doesn't have an aggregate function that would work here...
You could do a dependent subquery (for title and description) ; this would be OK to grab one row, but horribly slow if you want to grab many rows.
But you can also do something else ! That relies on some shady behaviour of MySQL's non-standard GROUP BY clause...
First, gather a list of "content_id"'s that you want to display (result of a paginated search query, whatever). Then you can could do something like the horror that follows :
SELECT * FROM
(
SELECT content_id, title FROM
(
SELECT c.content_id, c.title
FROM
content c
JOIN content_l10n cl USING (content_id)
JOIN l10n l USING (l10n_id)
LEFT JOIN user_l10n ul ON (ul.l10n_id=l.l10n_id AND ul.user_id=$user_id)
WHERE cl.content_id IN ($list) AND c.title IS NOT NULL
ORDER BY content_id, COALESCE(ul.order,l.order)
) d GROUP BY content_id
) t
JOIN
(
SELECT content_id, description FROM
(
SELECT c.content_id, c.description
FROM
content c
JOIN content_l10n cl USING (content_id)
JOIN l10n l USING (l10n_id)
LEFT JOIN user_l10n ul ON (ul.l10n_id=l.l10n_id AND ul.user_id=$user_id)
WHERE cl.content_id IN ($list) AND c.description IS NOT NULL
ORDER BY content_id, COALESCE(ul.order,l.order)
) d GROUP BY content_id
)
USING (content_id)
Upvotes: 4
Reputation: 8011
this should give you the basis for the second case:
SELECT content.url, content.date, content_l10n.title, content_l10n.description
FROM content, content_l10n, l10n
WHERE content.content_id = content_l10n.content_id AND
content_l10n.l10n_id = l10n.l10n_id AND
content.content_id = {$contentId}
ORDER BY l10n.order ASC
LIMIT 1
About performance, you need an index in content.content_id
, content_l10n.content_id
, l10n.l10n_id
and l10n.order
.
Upvotes: 1