diff --git a/composer.json b/composer.json index c0dceff..cf0a86d 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ "dragon-code/support": "^6.13", "illuminate/database": "^10.0 || ^11.0", "illuminate/support": "^10.0 || ^11.0", - "laravel-lang/config": "^1.6", + "laravel-lang/config": "^1.9", "laravel-lang/locales": "^2.9.2", "laravel/prompts": "^0.1.24" }, diff --git a/src/Eloquent/Scopes/FilterTranslationsScope.php b/src/Eloquent/Scopes/FilterTranslationsScope.php new file mode 100644 index 0000000..f9a7ccf --- /dev/null +++ b/src/Eloquent/Scopes/FilterTranslationsScope.php @@ -0,0 +1,34 @@ +enabled()) { + $builder->whereIn('locale', $this->locales()); + } + } + + protected function enabled(): bool + { + return Config::shared()->models->filter->enabled; + } + + protected function locales(): array + { + return array_unique([ + Locales::getCurrent()->code, + Locales::getFallback()->code, + ]); + } +} diff --git a/src/HasTranslations.php b/src/HasTranslations.php index 2fd01af..7d4e531 100644 --- a/src/HasTranslations.php +++ b/src/HasTranslations.php @@ -12,6 +12,7 @@ use LaravelLang\Locales\Facades\Locales; use LaravelLang\Models\Concerns\HasNames; use LaravelLang\Models\Concerns\ModelLoader; +use LaravelLang\Models\Eloquent\Scopes\FilterTranslationsScope; use LaravelLang\Models\Eloquent\Translation; use LaravelLang\Models\Services\Attribute; use LaravelLang\Models\Services\Registry; @@ -32,7 +33,8 @@ trait HasTranslations public function translations(): HasMany { - return $this->hasMany($this->translationModelName(), 'item_id'); + return $this->hasMany($this->translationModelName(), 'item_id') + ->tap(new FilterTranslationsScope()); } public function hasTranslated(string $column, Locale|LocaleData|string|null $locale = null): bool diff --git a/tests/Datasets/LocalesFilter.php b/tests/Datasets/LocalesFilter.php new file mode 100644 index 0000000..5aa38b3 --- /dev/null +++ b/tests/Datasets/LocalesFilter.php @@ -0,0 +1,28 @@ + [ + 'enabled' => true, + 'count' => 2, + + 'locales' => [ + FakeValue::LocaleFallback, + FakeValue::LocaleMain, + ], + ], + + 'disabled' => [ + 'enabled' => false, + 'count' => 3, + + 'locales' => [ + FakeValue::LocaleCustom, + FakeValue::LocaleFallback, + FakeValue::LocaleMain, + ], + ], +]); diff --git a/tests/Unit/Models/GetTest.php b/tests/Unit/Models/GetTest.php index 0862037..58b5078 100644 --- a/tests/Unit/Models/GetTest.php +++ b/tests/Unit/Models/GetTest.php @@ -2,8 +2,11 @@ declare(strict_types=1); -use App\Models\TestModel; use App\Models\TestModelTranslation; +use Illuminate\Database\Events\QueryExecuted; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Str; +use LaravelLang\Config\Enums\Name; use LaravelLang\Models\Exceptions\AttributeIsNotTranslatableException; use LaravelLang\Models\Exceptions\UnavailableLocaleException; use Tests\Constants\FakeValue; @@ -69,17 +72,43 @@ expect($model->getTranslation(FakeValue::ColumnTitle, FakeValue::LocaleCustom))->toBeNull(); }); -test('lazy loading', function () { +test('lazy loading', function (bool $enabled, int $count, array $locales) { + config()->set(Name::Shared() . '.models.filter.enabled', $enabled); + + $hasNonFilteredQuery = false; + $hasFilteredQuery = false; + + DB::listen(function (QueryExecuted $query) use (&$hasNonFilteredQuery, &$hasFilteredQuery) { + if (Str::is('select * where *."item_id" = ? and *."item_id" is not null', $query->sql)) { + $hasNonFilteredQuery = true; + } + + if (Str::is('select * where *."item_id" = ? and *."item_id" is not null and "locale" in (?, ?)', $query->sql)) { + $hasFilteredQuery = true; + } + }); + $model1 = fakeModel(main: 'Foo'); $model2 = fakeModel(main: 'Bar'); - TestModel::query()->get()->each( - fn (TestModel $model) => match ($model->getKey()) { - $model1->getKey() => expect($model->title)->toBe('Foo'), - $model2->getKey() => expect($model->title)->toBe('Bar'), - } - ); -}); + $model1->load('translations'); + $model2->load('translations'); + + expect($model1->relationLoaded('translations'))->toBeTrue(); + expect($model2->relationLoaded('translations'))->toBeTrue(); + + expect($model1->translations()->count())->toBe($count); + expect($model2->translations()->count())->toBe($count); + + expect($model1->translations->count())->toBe($count); + expect($model2->translations->count())->toBe($count); + + expect($model1->translations->pluck('locale')->sort()->values()->all())->toBe($locales); + expect($model2->translations->pluck('locale')->sort()->values()->all())->toBe($locales); + + expect($hasNonFilteredQuery)->toBe(! $enabled); + expect($hasFilteredQuery)->toBe($enabled); +})->with('locales-filter'); test('non-translatable attribute', function () { $key = fake()->word;