برای پیادهسازی قابلیت چندزبانه توی Laravel میتونیم از پکیج laravel-translatable از Spatie استفاده کنیم.
ایدهی این پکیج اینه که برای فیلدهای چندزبانه، بهجای اینکه برای هر زبان یه ستون جدا داشته باشیم، فقط یک ستون از نوع json داشته باشیم و ترجمهها رو داخل همون ستون نگه داریم.
اول پکیج رو نصب میکنیم:
composer require spatie/laravel-translatable
مثلاً فرض کنیم مدل Product داریم و میخوایم name و description چندزبانه باشن. توی migration، این فیلدها رو بهصورت json تعریف میکنیم:
$table->json('name');
$table->json('description');
بعد توی مدل مشخص میکنیم که این فیلدها قابل ترجمه هستن:
use Spatie\Translatable\HasTranslations;
class Product extends Model
{
use HasTranslations;
public array $translatable = [
'name',
'description',
];
}
حالا مقدار name میتونه چیزی شبیه این باشه:
{
"fa": "محصول جدید",
"en": "New product"
}
و میتونیم ترجمهها رو با خود مدل مدیریت کنیم:
$product->setTranslation('name', 'fa', 'محصول جدید');
$product->setTranslation('name', 'en', 'New product');
$product->save();
اینجوری اضافه شدن زبان جدید هم به تغییر ساختار جدول نیاز نداره؛ فقط ترجمهی زبان جدید به همون JSON اضافه میشه.
ایدهی اصلی پکیج هم دقیقاً همینه: چندزبانه بودن رو داخل خود فیلد مدیریت کنیم، نه با اضافه کردن ستون برای هر زبان.
توی لاراول، متد undot() برای تبدیل کلیدهای دارای . به یک ساختار تودرتو در Collection استفاده میشه.
فرض کنید تنظیمات یک کاربر رو به این شکل داریم:
$settings = collect([
'notifications.email' => true,
'notifications.sms' => false,
'appearance.theme' => 'dark',
]);
اگه بخوایم این دادهها به یک آرایهی تودرتو تبدیل بشن:
$settings = $settings->undot();
نتیجه:
[
'notifications' => [
'email' => true,
'sms' => false,
],
'appearance' => [
'theme' => 'dark',
],
]
با undot() میتونیم کلیدهای دارای . رو به ساختار تودرتو تبدیل کنیم، بدون اینکه خودمون این ساختار رو بسازیم.
وقتی میدونیم قراره از رابطهی یک مدل استفاده کنیم، بهتره اون رابطه رو از همون اول با Eager Loading دریافت کنیم.
مثلاً اگه قراره نویسندهی هر پست رو نمایش بدیم:
$posts = Post::with('author')->get();
این کار باعث میشه لاراول رابطهی "author" رو از قبل دریافت کنه و برای هر پست یک کوئری جدا اجرا نکنه.
به این شکل میتونیم جلوی مشکل N+1 Query رو بگیریم.
🧬 تیبلهای مورف (Polymorphic) چی هستن؟
وقتی میخوای یک مدل رو به چند مدل مختلف وصل کنی، بدون اینکه برای هر کدوم یک جدول جدا بسازی، سراغ رابطهی مورف (Polymorphic Relation) میری.
مثال کلاسیک: یک جدول comments که هم میتونه به posts تعلق داشته باشه، هم به videos.
🤔 چرا بهجاش دو تا فارینکی نمیذاریم؟
راه سنتی اینه که دو ستون جدا بذاری:
post_id
video_id
اما این روش با اضافهشدن هر مدل جدید، جدولت رو کثیفتر میکنه و همیشه یکی از ستونها NULL میمونه. 🙃
✅ راهحل: دو ستون جادویی
در تیبل مورف، بهجای چند فارینکی، فقط دو ستون داری:
| ستون | کاربرد |
|---|---|
commentable_id |
آیدی رکورد مرتبط |
commentable_type |
نوع مدل (مثلاً Post یا Video) |
// در Laravel Eloquent
public function commentable()
{
return $this->morphTo();
}
با این ترفند، یک جدول comments میتونه به بینهایت نوع مدل وصل بشه، بدون اینکه ساختارش عوض بشه.
⚖️ نکات مهم قبل از استفاده
- ✳️ ایندکس گذاشتن روی
(*_id, *_type)رو فراموش نکن، وگرنه کوئریها کند میشن. - ✳️ برای رابطههای دوطرفه (مثل تگها بین چند مدل) از
morphToManyاستفاده کن. - ✳️ اگه رابطهی مورف زیاد پیچیده شد، شاید بهتره ساختارت رو دوباره بازبینی کنی؛ گاهی EAV یا جدولهای جدا خواناتره.
🎯 جمعبندی
تیبلهای مورف یه ابزار قدرتمندن برای وقتی که چند مدل باید یک رفتار مشترک (مثل کامنت، لایک، تگ) داشته باشن — اما مثل هر ابزار قدرتمندی، باید جای درستش استفاده بشه، نه همهجا! 🚀
تا حالا با متد avg تو لاراول کار کردید یا میدونستید چه کاربردی داره؟
این متد میانگین یه فیلد مشخص رو حساب میکنه..
فرض کنید یه محصول داریم و چندتا review براش ثبت شده، هر review شامل این فیلدهاست:
[
['rating' => 4, 'comment' => 'خوب بود', 'approved' => true],
['rating' => 5, 'comment' => 'عالی 👌', 'approved' => true],
['rating' => 3, 'comment' => 'میتونست بهتر باشه', 'approved' => false],
]اگه با Collection کار میکنید:
$avgRating = $product->reviews
->where('approved', true)
->avg('rating');و اگه با Query Builder:
$avgRating = $product->reviews()
->where('approved', true)
->avg('rating');هم Collection و هم Query Builder، میانگین رو بهسادگی محاسبه میکنن:
// خروجی هر دو:
4.5