diff --git a/src/HasTranslations.php b/src/HasTranslations.php index 01efb1a..15cc08a 100644 --- a/src/HasTranslations.php +++ b/src/HasTranslations.php @@ -65,7 +65,7 @@ public function getTranslation(string $key, string $locale, bool $useFallbackLoc $translations = $this->getTranslations($key); - $translation = $translations[$normalizedLocale] ?? ''; + $translation = $translations[$normalizedLocale] ?? null; $translatableConfig = app(Translatable::class); @@ -101,20 +101,20 @@ public function getTranslationWithoutFallback(string $key, string $locale): mixe return $this->getTranslation($key, $locale, false); } - public function getTranslations(string $key = null, array $allowedLocales = null): array + public function getTranslations(string $key = null, array $allowedLocales = null, bool $keepNullValues = true): array { if ($key !== null) { $this->guardAgainstNonTranslatableAttribute($key); return array_filter( json_decode($this->getAttributes()[$key] ?? '' ?: '{}', true) ?: [], - fn ($value, $locale) => $this->filterTranslations($value, $locale, $allowedLocales), + fn ($value, $locale) => $this->filterTranslations($value, $locale, $allowedLocales, $keepNullValues), ARRAY_FILTER_USE_BOTH, ); } - return array_reduce($this->getTranslatableAttributes(), function ($result, $item) use ($allowedLocales) { - $result[$item] = $this->getTranslations($item, $allowedLocales); + return array_reduce($this->getTranslatableAttributes(), function ($result, $item) use ($allowedLocales, $keepNullValues) { + $result[$item] = $this->getTranslations($item, $allowedLocales, $keepNullValues); return $result; }); @@ -204,7 +204,7 @@ public function forgetAllTranslations(string $locale): self public function getTranslatedLocales(string $key): array { - return array_keys($this->getTranslations($key)); + return array_keys($this->getTranslations($key, null, false)); } public function isTranslatableAttribute(string $key): bool @@ -268,14 +268,16 @@ protected function normalizeLocale(string $key, string $locale, bool $useFallbac return $locale; } - protected function filterTranslations(mixed $value = null, string $locale = null, array $allowedLocales = null): bool + protected function filterTranslations(mixed $value = null, string $locale = null, array $allowedLocales = null, bool $keepNullValues = true): bool { - if ($value === null) { - return false; - } + if (! $keepNullValues) { + if ($value === null) { + return false; + } - if ($value === '') { - return false; + if ($value === '') { + return false; + } } if ($allowedLocales === null) { diff --git a/tests/TranslatableTest.php b/tests/TranslatableTest.php index 50a5ee0..e59ff8e 100644 --- a/tests/TranslatableTest.php +++ b/tests/TranslatableTest.php @@ -39,7 +39,7 @@ $this->testModel->setTranslation('name', 'en', 'testValue_en'); $this->testModel->save(); - expect($this->testModel->getTranslation('name', 'fr', false))->toBe(''); + expect($this->testModel->getTranslation('name', 'fr', false))->toBe(null); }); it('will return fallback locale translation when getting an unknown locale and fallback is true', function () { @@ -126,7 +126,7 @@ $this->testModel->setTranslation('name', 'en', 'testValue_en'); $this->testModel->save(); - expect($this->testModel->getTranslationWithoutFallback('name', 'fr'))->toBe(''); + expect($this->testModel->getTranslationWithoutFallback('name', 'fr'))->toBe(null); }); it('will return an empty string when getting an unknown locale and fallback is empty', function () { @@ -139,7 +139,7 @@ $this->testModel->setTranslation('name', 'en', 'testValue_en'); $this->testModel->save(); - expect($this->testModel->getTranslation('name', 'fr'))->toBe(''); + expect($this->testModel->getTranslation('name', 'fr'))->toBe(null); }); it('can save a translated attribute', function () { @@ -149,6 +149,13 @@ expect($this->testModel->name)->toBe('testValue_en'); }); +it('can save null value in a translated attribute', function () { + $this->testModel->setTranslation('name', 'en', null); + $this->testModel->save(); + + expect($this->testModel->name)->toBe(null); +}); + it('can set translated values when creating a model', function () { $model = TestModel::create([ 'name' => ['en' => 'testValue_en'], @@ -448,6 +455,7 @@ public function setNameAttribute($value) it('can check if an attribute has translation', function () { $this->testModel->setTranslation('name', 'en', 'testValue_en'); $this->testModel->setTranslation('name', 'nl', null); + $this->testModel->setTranslation('name', 'de', null); $this->testModel->save(); expect($this->testModel->hasTranslation('name', 'en'))->toBeTrue(); @@ -455,6 +463,15 @@ public function setNameAttribute($value) expect($this->testModel->hasTranslation('name', 'pt'))->toBeFalse(); }); +it('will return the same number of translations with the same values as saved', function () { + $this->testModel->setTranslation('name', 'en', 'testValue_en'); + $this->testModel->setTranslation('name', 'nl', null); + $this->testModel->setTranslation('name', 'de', ''); + $this->testModel->save(); + + expect($this->testModel->getTranslations('name'))->toEqual(['en' => 'testValue_en', 'nl' => null, 'de' => '']); +}); + it('can correctly set a field when a mutator is defined', function () { $testModel = (new class () extends TestModel { public function setNameAttribute($value) @@ -699,7 +716,7 @@ public function setAttributesExternally(array $attributes) $this->testModel->save(); $this->testModel->setLocale('it'); - expect($this->testModel->getTranslation('name', 'it', false))->toBe(''); + expect($this->testModel->getTranslation('name', 'it', false))->toBe(null); }); it('will return default fallback locale translation when getting an unknown locale with fallback any', function () { @@ -760,7 +777,7 @@ public function setAttributesExternally(array $attributes) $model->setLocale('fr'); - expect($model->name)->toBe(''); + expect($model->name)->toBe(null); }); it('can set fallback locale on model', function () {