I'm developing a Multilanguage API with laravel 6 and I've got this situation in my database:
Categories
id
other not relevants fields
Languages:
id
name
code
Category_Language
id
language_id
category_id
name --> this is the name of the category in the specific language.
Now I've 2 models, the first for Category
class Category extends Model
{
public function languages()
{
return $this->belongsToMany('App\Models\v1\Language')->withTimestamps()->withPivot('name');
}
}
and the second Model
class Language extends Model
{
protected $fillable = ['code', 'name', 'image_id', 'enabled'];
public function categories() {
return $this->belongsToMany('App\Models\v1\Category')->withTimestamps()->withPivot('name');
}
}
In my logic (I'm using Service Pattern) I've used this approach on create Category, every time I Pass a JSON object like this:
{
"names": [
{
"languageId": 1,
"name": "Hello"
},
{
"languageId": 2,
"name": "Hola"
}
]
}
First of all I create a category (validate if the id of the language is really stored in database) and then with many to many laravel power I attach the language and the name in the category model like this:
foreach($request->names as $name) {
$category->languages()->attach($name['languageId'], ['name' => $name['name']]);
}
Now this seems to work perfectly and it's good to retrieve all the category without filtering languages using API Resource like this:
public function toArray($request)
{
$category = [];
$category['id'] = $this->id;
$category['languages'] = [];
$category['languages'] = $this->languages->map(function ($language) {
return [
'languageId' => $language->id,
'languageCode' => $language->code,
'languageName' => $language->name,
'categoryName' => $language->pivot->name,
];
});
return $category;
}
and this is the output for 3 categories with 3 languages (faker data):
array:3 [
"data" => array:3 [
0 => array:4 [
"id" => 1
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "pa"
"languageName" => "Kacey Trantow"
"categoryName" => "quasi"
]
1 => array:4 [
"languageId" => 2
"languageCode" => "ne"
"languageName" => "Mr. Alexandre Heathcote"
"categoryName" => "perferendis"
]
2 => array:4 [
"languageId" => 3
"languageCode" => "kj"
"languageName" => "Mr. Misael Robel"
"categoryName" => "repudiandae"
]
]
"imageUrl" => null
"enabled" => true
]
1 => array:4 [
"id" => 2
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "pa"
"languageName" => "Kacey Trantow"
"categoryName" => "non"
]
1 => array:4 [
"languageId" => 2
"languageCode" => "ne"
"languageName" => "Mr. Alexandre Heathcote"
"categoryName" => "vitae"
]
2 => array:4 [
"languageId" => 3
"languageCode" => "kj"
"languageName" => "Mr. Misael Robel"
"categoryName" => "suscipit"
]
]
"imageUrl" => null
"enabled" => true
]
2 => array:4 [
"id" => 3
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "pa"
"languageName" => "Kacey Trantow"
"categoryName" => "molestiae"
]
1 => array:4 [
"languageId" => 2
"languageCode" => "ne"
"languageName" => "Mr. Alexandre Heathcote"
"categoryName" => "esse"
]
2 => array:4 [
"languageId" => 3
"languageCode" => "kj"
"languageName" => "Mr. Misael Robel"
"categoryName" => "beatae"
]
]
"imageUrl" => null
"enabled" => true
]
]
now, the problem is when I want to FILTER the categories passing ONLY 1 specific Language, a classical use case when a user use ONLY 1 language during navigations so, If I want ONLY 1 language (and 1 name in pivot relations of category) what kind of operation I need?
I've organized the service with specific filters and ordering for any collections but this kind of need seems to make me crazy!
So this is my final getCategories method that build filter, ordering and include dinamically:
-
imaging so to have a query string like this:
http://localhost:8000/api/v1/categories?orderBy=id:asc&include=language&language.code=EN
and I want a response like this:
array:3 [
"data" => array:3 [
0 => array:4 [
"id" => 1
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "EN"
"languageName" => "English"
"categoryName" => "quasi"
]
]
]
1 => array:4 [
"id" => 2
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "EN"
"languageName" => "English"
"categoryName" => "non"
]
]
]
2 => array:4 [
"id" => 3
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "EN"
"languageName" => "English"
"categoryName" => "molestiae"
]
]
]
]
I need something like this but GENERALLY, because we have others entities that use the same logic for multilanguage system.
To clarify what I want is like a join query like this:
SELECT *
FROM categories ca INNER JOIN category_language cl ON ca.id =cl.category_id
INNER JOIN languages lan ON lan.id = cl.language_id
WHERE lan.code = 'EN';
You will retrieve a list of categories only in ENGLISH languages...
Thanks for any suggestion and help!