We have a need to store user provided translations, and the current design approached the problem in what I thought was a sufficient structure:
id | language_id (FK) | item_id (FK) | translation
So, if I have item A with ID 1, with a translation for language ID 2, it’d show up as:
1 | 2 | 1 | Hello World in Spanish
Now lets say another user inherits item A’s properties (call it item B), such as that B has attribute parent_id
as well, pointing to 1 (which is A)
This way, in Laravel, the following works:
public function translations(){
$language_ids = $this->item_languages()->pluck('language_id')->toArray();
$item_ids = [$this->id];
if ($this->hasParentItem()){
array_push($item_ids, $this->parent_id);
$parent_item = $this->parentItem()->first();
$direct_parent_item = $parent_item;
while ($parent_item->hasParentItem()) {
array_push($item_ids, $parent_item->parent_id);
$parent_item = $parent_item->parentItem()->first();
}
}
return Translation::whereIn('language_id', $language_ids)->whereIn('item_id', $item_ids);
}
All looked good!
Until we realized in QA that grand children dont work in the front end, and no wonder!
Item C is born into the world!
It supposed (and does) inherit the grand parent’s A translation, and so the above code returns translation with ID 1.
However, Item C’s own model, only has direct reference to parent_id
2 and not 3!
Therefore, the front end mapping and showing translations, has no way to know the translation actually belongs to current Item C being used!
I am looking for two possible solutions:
-
a proper solution, which may force to migrate existing data already used in the system 🙁 I hope to avoid this although it may not be too bad depending on the solution.
-
Alternatively, what I was thinking is to add accessor and mutator to an appended attribute in the translation model, and set extra attribute
direct_parent
, to each translation in the retrieved collection, to the direct parent in the current context (using$direct_parent_item
in the example code).
The downside is that it is somewhat convoluted and confusing without the above context in the future, but would allow quick mapping on the front end.
Would this work? Do you have a better alternative how to modify the original design as stated above in a proper manner?
Duplicating the Item A translation is not an option, because we need to allow A to also control and update the translations inherited.
2
Answers
In my opinion a better way to store this data is using a json object. you can store your data like
and if this is a table in a relational database you can use JSON object just for parents part like:
if you need to store multi level deep parent relations you need a new table that links each parent to all of it’s children, something like:
each time a new model is created you can loop the
parent_id
‘s in a recursive function and insert them in this table keeping track ofthrough
anddepth
. And on the model you would have 3 relations:parents->first()
would be the same as->parent
,parents()->whereHasTranslation($key)->first()
, wherewhereHasTranslation
is some custom scope you can implement, would give you the closest, or furthest depending or your order direction bydepth
, that has a translation key