Suggestion for database structure for multilanguage - php

Using PHP 5.x and MySQL 5.x
So I asked a question yesterday about the best way of handling dynamic data in multilanguage, this question is what would be a good solution for the database structure to handle this. I have entries in the database for things like stories. My plan is to store a different version of the story for each language available in a db. So if the site supports english and spanish then in the admin tool they can add a english version of a story and a spanish version of a story.
My thoughts on that was to have seperate tables in the db, one for each language. So one story table for english version, one for spanish and one for whatever other languages. Then on the front end I simply allow the visitor to select what language to view the site in and via variables know what table to query to get that version of the story. But one issue is what if there isnt a spanish version but they selected spanish? Is that a good solution or is there a better way of doing this? Looking for suggestions.

Having multiple tables is really not necessary, unless you plan on having millions of stories... I usually go along with two tables; one for the item and another for the localized item
For example, a story would be like
table "story"
id INTEGER (Primary Key)
creation_date DATE_TIME
author VAR_CHAR(32)
..other general columns..
table "story_localized"
story_id INTEGER (Foreign Key of story.id) \
lang CHAR(2) -- (Indexed, unique -- story_id+lang)
content TEXT
..other localized columns..
Performing the query is simply a matter of JOINing the two tables :
SELECT s.*, sl.*
FROM story s
JOIN story_localized sl ON s.id = sl.story_id
WHERE s.id = 1 -- the story you're looking for here
AND sl.lang = 'en' -- your language here
-- other conditions here
This configuration gives a few advantages :
all your data is in the same table, no need to synchronizing CRUD operations
you can add new languages to any story without the need to create yet more tables
etc.
** EDIT **
Just as a bonus, here is a "trick" to retrieve a story easily, regardless of the language it's been written into
SELECT *
FROM (SELECT *
FROM story s
JOIN story_localized sl ON s.id = sl.story_id
WHERE s.id = {storyId}
AND sl.lang = {desiredLanguage} -- example : 'es'
UNION
SELECT *
FROM story s
JOIN story_localized sl ON s.id = sl.story_id
WHERE s.id = {storyId}
AND sl.lang = {defaultLanguage} -- example : 'en'
UNION
SELECT *
FROM story s
JOIN story_localized sl ON s.id = sl.story_id
WHERE s.id = {storyId}
LIMIT 1 -- ..get the first language found
) story_l
LIMIT 1 -- only get the first SELECTed row found
Will try to fetch the story in the {desiredLanguage}, if the story is not available in that language, try to find it in {defaultLanguage} (ie. the site's default language), and if still nothing found, it doesn't matter which language to fetch the story, then, so fetch the first one found. All in one query, and all you need are 3 arguments: the story id, the desired language, and a default fallback language.
Also, you can easily find out in what language the story is available into with a simple query :
SELECT sl.lang
FROM story_localized sl
WHERE sl.story_id = {storyId}

The best way to do this is to store the story as a multi-valued attribute. As #Yanick has shown you.
And it would be interfacially better if you populate your language choice element only with those languages in which that story is available.

Another nice approach when it is not a long text: weeks names, monts... in my app I am localizing musical genres, and musical instruments. Use a text field for store json data.
So in my musical "genres" table, there is a text field named "lang". On in I have the following json structure:
{"es_gl":"MARCHA ESPAÑOLA", "es_EN" : "SPANISH MARCH"}
Then using PHP is really easy to get our data from a json structure. Let's say I have a genre on my $genre variable.
$myJSON = json_decode($genre->lang);
echo $myJSON->es_gl // This echoes MARCHA ESPAÑOLA
echo $myJSON->es_es // This echoes SPANISH MARCH
This is good solution when your localization data is not big... like posts or blogs.

Related

Good practice for handling naturally JOINed results across an application

I'm working on an existing application that uses some JOIN statements to create "immutable" objects (i.e. the results are always JOINed to create a processable object - results from only one table will be meaningless).
For example:
SELECT r.*,u.user_username,u.user_pic FROM articles r INNER JOIN users u ON u.user_id=r.article_author WHERE ...
will yield a result of type, let's say, ArticleWithUser that is necessary to display an article with the author details (like a blog post).
Now, I need to make a table featured_items which contains the columnsitem_type (article, file, comment, etc.) and item_id (the article's, file's or comment's id), and query it to get a list of the featured items of some type.
Assuming tables other than articles contain whole objects that do not need JOINing with other tables, I can simply pull them with a dynamicially generated query like
SELECT some_table.* FROM featured_items RIGHT JOIN some_table ON some_table.id = featured_items.item_id WHERE featured_items.type = X
But what if I need to get a featured item from the aforementioned type ArticleWithUser? I cannot use the dynamically generated query because the syntax will not suit two JOINs.
So, my question is: is there a better practice to retrieve results that are always combined together? Maybe do the second JOIN on the application end?
Or do I have to write special code for each of those combined results types?
Thank you!
a view can be thot of as like a table for the faint of heart.
https://dev.mysql.com/doc/refman/5.0/en/create-view.html
views can incorporate joins. and other views. keep in mind that upon creation, they take a snapshot of the columns in existence at that time on underlying tables, so Alter Table stmts adding columns to those tables are not picked up in select *.
An old article which I consider required reading on the subject of MySQL Views:
By Peter Zaitsev
To answer your question as to whether they are widely used, they are a major part of the database developer's toolkit, and in some situations offer significant benefits, which have more to do with indexing than with the nature of views, per se.

select table name to join from SQL query

In current database design, I have a main table called "Leads" and some other tables starting with product_ ( product_life_insurance, product_medical_insurance , ... )
-Leads Table :
--ID
--Product
...
-Product_Life_insurance Table :
--ID
--LeadID
...
a Lead Row :
ID 5
Product: life_insurance
a product_life_insurance Row:
ID 1
LeadId 5
..
Is there anyway to create a query to select table Name from Leads and add "product_" prefix to it and then join it to product table ?
I mean :
SELECT *
FROM `leads` JOIN `product_life_insurance` ON `leads`.`id` = `product_life_insurance`.`leadID`
WHERE `leads`.`id` = '5';
I want to select table name for join from leads table and add "product_" prefix to it and use it in my query.
Thanks :)
You asked:
Is there any way to create a query to select table Name from Leads and
add the "product_" prefix to it and then join it to product table ?
The answer is no. In pure SQL, you can't make variables of table names and then use them in your queries. You can, of course, do this in your host php code. But you'll need to use one query to fetch the table names, and more queries to fetch the results from that table name.
You can also use Dynamic SQL. That's a MySQL feature allowing you to create the text of SQL queries dynamically in the MySQL server, and then run those queries.
It sounds to me like you're trying to store several classes of entities (life insurance, annuities, vehicle insurance, others) having widely differing attributes.
This presents you with some schema-design options.
Should you use different tables (as you are doing), showing the
entity (lead) class (life insurance) in a master table, and joining
the particular table you need?
Should you try to coerce all the attributes into a single entity,
leaving NULL or blank the attributes that are irrelevant for a
particular class of entity?
Should you use a key/value store for your entities, the way
WordPress's wp_postmeta table does?
Option 3 has a disadvantage if you do a lot of searching on attribute values: it requires your attributes to all be stored with the same data type. That data type is probably varchar(n). That means that it's hard to search on ranges of numeric attribute values. For example '10' is BETWEEN '1' AND '9' considered as text, but that's nonsense numerically. You can beat that problem using implicit typecasting, but that defeats the use of an index. That is,
0+meta_value BETWEEN 0 AND 9
forces the comparison to work numerically on the meta_value column. It works, but not fast. That being said, Option 3 is the most flexible by far; you can add new attributes without changing table definitions.
A combination of Option 2 and Option 3, putting the most commonly searched attribute values into your main lead table, will probably yield the most robust solution.
Option 1 -- your present solution -- is a performance nightmare waiting to attack you when you can least afford it: as your application is scaling up.
NOTE: if you are using the MariaDB fork of MySQL your key_value table can contain a persistent, indexed, virtual column. For example,
meta_key VARCHAR(255),
meta_value VARCHAR(255),
meta_value_int BIGINT(20) AS (0+meta_value) PERSISTENT,
meta_value_float FLOAT AS (CAST(meta_value AS DECIMAL(30,10))) PERSISTENT
You can then index those virtual (defined with AS) columns and search them fast. meta_value columns that don't start with numbers will have the value 0 in the virtual columns.

Phrases MySQL in two languages

how are you guys? I'm trying to make my PHP script accept many languages, and give the admin the option of change the Phrases in the script.
So, I found that many people store the Phrases in the database, but my question is do I need to SELECT all the phrases from my database each time I opened a page ? or what I have to do?
Phrase EN FR
-----------------------------------------------------------------------
err_no_page There was an error. il y avait une erreur.
in each page I need to use maybe phrases, what is the perfect method to get them from the database without exhausting the server.
Thank's in advance.
You should normalize your database like this:
PHRASES
ID DESCRIPTION
---- ----------------
1 'err_no_page'
2 'another phrase'
TRANSLATIONS
ID PHRASE_ID LANGUAGE_ID TRANSLATION
-- --------- ----------- -------------------------
1 1 'en' 'There was an error.'
2 1 'fr' 'il y avait une erreur.'
3 2 'en' 'etc'
Then you can just select all the translations for a single language directly:
SELECT
p.description, t.translation
FROM
phrases p
INNER JOIN translations t ON t.phrase_id = p.id
WHERE
t.language_id = 'en'
The benefit of doing it this way is that you can easily add more languages without changing the structure of your database.
If these phrases are used frequently (as they probably are in a web app) then you should consider reading them in once into some kind of in-memory dictionary cache when your application starts up, rather than retrieving them from the database for each page load.
EDIT
Check out the PHP module memcache for a possible way to implement your cache.
I hope you will not show site in all phrases at a time.
So it will be better to give a option somewhere on site to change language, and run your query to fetch phrases according to selected language at a time instead of selecting all.

How to handle hardcoded stuffs in database and coding

There are some language and courses(based on language) are defined in two table. Language table reference is used in course table to relate course with particular language. I also have a notes table that content notes of specific course and that is related to course table. Now I have two issues.
Now in coding I need to take some specific action for Spanish language only. So how should I handle this as languages will be entered by users and we would not be having any idea about Spanish language ID. If I do use text (the language name) then each time I need to fetch ID for Spanish from language table and then will fetch all course related to this from course table.
Suppose Spanish notes are stored in four separate sections and other notes have only one section so should I use same table with four column (one for each section) or use two tables(notes and spanish_notes). Using former way, will leave three column blank for other languages notes. I don't think that is good.
One quick solution to your first issue about multiple languages is to use language codes such as 'en', 'es', 'fr' etc. For instance in your language table you could have both id, code columns but in your content tables you could have a FK with code. So you could either get this lang. code from requests Accept-Language property. or somewhere else.
For second question in terms of normalization it is better to have separate tables for Spanish notes. It is way better for many reasons such as redundancy and dependency concerns.
EDIT: PS. You could also have a look at language codes from here and HTTP Accept-Language from here.
Some inputs:
There could be 2 ways of doing this:
a. When languages are entered by users, use a SELECT dropdown box for accepting user inputs. For each SELECT option, you can set the language name as the text and language id as the value. This way you will know the language ID as
b. You can use MySQL INNER JOIN between "language" and "courses" table, something like:
SELECT *
FROM `language` `l`
INNER JOIN `courses` `c` ON `l`.`language_id` = `c`.`language_id`
WHERE `l`.`language_name` = `spanish`;
I think it's okay to keep all notes for all sections in the single table. So, for the other 3 columns that will only contain Spanish notes, you can set them to accept NULL values
Hope it helps.

Best way to store and search keywords for a record in PHP and MySQL?

I haven't touched any code in a good 4-5 months so just getting back into it today, usually takes me a week or so to get all the info flowing through my brain again once I take months off like that. So my project I am about to start will be a PHP/MySQL backend bookmarks database.
I want to create a nice searchable database with all my favorite websites/bookmarks. Each record will have multiple keywords assigned to it so I can easily search all my bookmarks for the term "php" and all records with "php" in there keyword column or title or otherwise will come back in a result set.
Here is my idea for the database so far...
auto_id = /*Auto incremented ID number for database*/
name/title = /*Name/title of the Website*/
description = /*brief description of the site*/
URL = /*URL to open when I click a link*/
clicks = /*increments by 1 everytime I click the link*/
date_created = /*datetime that URL bookmark was added*/
date_accessed = /*datetime field for when last clicked on*/
category = /*category name or number to create a folder like structure of bookmarks in groups*/
sub_category = /*some categories will have subcategories (ie programming->c## programming->PHP )*/
keywords = /*Keywords used for searching*/
This is pretty straight forward for me on how to build this system all except I am looking for help/advice on the best way to store the keywords. Each website/record I add to the DB can have 1 up to multiple keywords per site. These keywords need to be able to help with the searching part of my app. So how should I store keywords for a site in my database? I know I could just have a "keywords" row in the table and store the keywords for each record like this "php, web, etc, keyword4" so all keywords for each site are saved in 1 column but this does not seem to be the best method when it comes to searching the database.
Please tell me how you would do this part? Thanks for any help
The best way to do this is to create a separate table to contain your keywords and then add an intersection (or join) table to join keywords with bookmarks.
CREATE TABLE bookmarks (
id INT NOT NULL,
... etc.
)
CREATE TABLE keywords (
id INT NOT NULL,
... etc.
)
CREATE TABLE bookmark_keywords (
bookmark_id INT NOT NULL,
keyword_id INT NOT NULL,
PRIMARY KEY (bookmark_id, keyword_id),
FOREIGN KEY bookmark_id REFERENCES bookmarks (id),
FOREIGN KEY keyword_id REFERENCES keywords (id)
)
When you insert a bookmark, you'd also insert any keywords that are being used and aren't already in the keywords table, as well as a row in bookmark_keywords in order to join the keyword with the bookmark.
Then, when you want to query for what keywords a bookmark has:
SELECT k.*
FROM keywords AS k
LEFT JOIN bookmark_keywords AS kb
ON kb.keyword_id = k.id
WHERE kb.bookmark_id = [ID of the bookmark]
And to query for what bookmarks share a particular keyword:
SELECT b.*
FROM bookmarks AS b
LEFT JOIN bookmark_keywords AS kb
ON kb.bookmark_id = b.id
WHERE kb.keyword_id = [ID of the keyword]
You're right, storing a comma-separated list in one column is not a good way to do it (this is called a repeating group and it violates the First Normal Form of relational database design).
Using a LIKE predicate is not a good choice, because it cannot benefit from an index. Searching for keywords this way is hundreds or thousands of times slower than designing a proper database in normal form, and adding indexes.
You need to store a second table listing keywords, and a third many-to-many table to pair keywords to applicable bookmarks. This is a pretty standard design for "tagging" in a relational database.
In non-relational databases like CouchDB or MongoDB, you can make one field a set of keywords, and index them so queries can be efficient. But not in a relational database.
See also:
How do you recommend implementing tags or tagging
Database Design for Tagging
Also when viewing those questions, check the many related questions in the column on the right.
The easiest, and fastest, search technique to implement is the use of MySQL's LIKE statement. LIKE lets you search through a column for a specific string. Consider the following example...
auto_id name description
1 Cool PHP Site you know you love it
2 PLARP! its Ruby gems gems gems!
3 SqlWha sql for the masses
4 FuzzD00dle fun in the sun, with some fuzz
You could find all rows that contain the string 'php' in either the 'name' or 'description' field using the following query...
SELECT * FROM bookmarks WHERE name LIKE '%php%' OR description LIKE '%php%';
'%' is a wildcard character.
Reference on MySQL LIKE: http://www.tutorialspoint.com/mysql/mysql-like-clause.htm
You could also add a 'keywords' column and store the keywords in a comma delimited format (ie: plarp1, plarp2, plarp3), then search through that.

Categories