diff --git a/composer.json b/composer.json index 1759a76..80a1fba 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ "illuminate/http": "^10.0 || ^11.0", "illuminate/routing": "^10.0 || ^11.0", "illuminate/support": "^10.0 || ^11.0", - "laravel-lang/config": "^1.5", + "laravel-lang/config": "^1.6", "laravel-lang/locales": "^2.8" }, "require-dev": { diff --git a/phpunit.xml b/phpunit.xml index 2829ec8..7c7083b 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -20,5 +20,8 @@ + + + diff --git a/src/Middlewares/LocalizationByModel.php b/src/Middlewares/LocalizationByModel.php new file mode 100644 index 0000000..f9831b8 --- /dev/null +++ b/src/Middlewares/LocalizationByModel.php @@ -0,0 +1,48 @@ +has($user = $this->user($request))) { + return $user->getAttribute($this->attribute()); + } + + return null; + } + + protected function has(?Model $user): bool + { + return $user && $this->hasAttribute($user); + } + + protected function user(Request $request): ?Model + { + return $request->user($this->guard); + } + + protected function attribute(): string + { + return $this->names()->column; + } + + protected function hasAttribute(Model $user): bool + { + if (method_exists($user, 'hasAttribute')) { + return $user->hasAttribute($this->attribute()); + } + + return array_key_exists($this->attribute(), $user->getAttributes()); + } +} diff --git a/src/Services/Route.php b/src/Services/Route.php index 0eed673..27a8815 100644 --- a/src/Services/Route.php +++ b/src/Services/Route.php @@ -10,6 +10,7 @@ use LaravelLang\Routes\Helpers\Route as RouteName; use LaravelLang\Routes\Middlewares\LocalizationByCookie; use LaravelLang\Routes\Middlewares\LocalizationByHeader; +use LaravelLang\Routes\Middlewares\LocalizationByModel; use LaravelLang\Routes\Middlewares\LocalizationByParameterPrefix; use LaravelLang\Routes\Middlewares\LocalizationBySession; @@ -23,6 +24,7 @@ public function group(Closure $callback): void LocalizationByCookie::class, LocalizationByHeader::class, LocalizationBySession::class, + LocalizationByModel::class, ])->group($callback); BaseRoute::prefix('{' . $this->names()->parameter . '}') @@ -32,6 +34,7 @@ public function group(Closure $callback): void LocalizationByCookie::class, LocalizationByHeader::class, LocalizationBySession::class, + LocalizationByModel::class, ])->group($callback); } } diff --git a/tests/Concerns/Routes.php b/tests/Concerns/Routes.php index d553cd2..c1a7542 100644 --- a/tests/Concerns/Routes.php +++ b/tests/Concerns/Routes.php @@ -8,6 +8,7 @@ use LaravelLang\Routes\Facades\LocalizationRoute; use LaravelLang\Routes\Middlewares\LocalizationByCookie; use LaravelLang\Routes\Middlewares\LocalizationByHeader; +use LaravelLang\Routes\Middlewares\LocalizationByModel; use LaravelLang\Routes\Middlewares\LocalizationByParameter; use LaravelLang\Routes\Middlewares\LocalizationByParameterWithRedirect; use LaravelLang\Routes\Middlewares\LocalizationBySession; @@ -53,6 +54,16 @@ public function setUpRoutes(): void ->get('session/{foo}', $this->jsonResponse()) ->name('via.session'); + app('router') + ->middleware(LocalizationByModel::class) + ->get('model/default/{foo}', $this->jsonResponse()) + ->name('via.model.default'); + + app('router') + ->middleware(LocalizationByModel::class . ':foo') + ->get('model/guard/{foo}', $this->jsonResponse()) + ->name('via.model.guard'); + app('router') ->middleware([ LocalizationByParameter::class, @@ -60,6 +71,7 @@ public function setUpRoutes(): void LocalizationByHeader::class, LocalizationByCookie::class, LocalizationBySession::class, + LocalizationByModel::class, ]) ->get('clean/{foo}', $this->jsonResponse()) ->name('clean'); diff --git a/tests/Feature/Middlewares/ModelAuthorizedGuardTest.php b/tests/Feature/Middlewares/ModelAuthorizedGuardTest.php new file mode 100644 index 0000000..8762ae8 --- /dev/null +++ b/tests/Feature/Middlewares/ModelAuthorizedGuardTest.php @@ -0,0 +1,68 @@ +assertSuccessful() + ->assertJsonPath($foo, LocaleValue::TranslationFrench); + + assertEventNotDispatched(); +}); + +test('aliased locale', function (string $locale) { + actingAs(fakeUser($locale)); + + $foo = 'test'; + + getJson(route('via.model.guard', compact('foo', 'locale'))) + ->assertSuccessful() + ->assertJsonPath($foo, LocaleValue::TranslationGerman); + + assertEventDispatched(); +})->with('aliased-locales'); + +test('empty locale', function (int|string|null $locale) { + actingAs(fakeUser($locale)); + + $foo = 'test'; + + getJson(route('via.model.guard', compact('foo', 'locale'))) + ->assertSuccessful() + ->assertJsonPath($foo, LocaleValue::TranslationFrench); + + assertEventNotDispatched(); +})->with('empty-locales'); + +test('uninstalled locale', function (string $locale) { + actingAs(fakeUser($locale)); + + $foo = 'test'; + + getJson(route('via.model.guard', compact('foo', 'locale'))) + ->assertSuccessful() + ->assertJsonPath($foo, LocaleValue::TranslationFrench); + + assertEventDispatched(); +})->with('uninstalled-locales'); + +test('unknown locale', function (int|string $locale) { + actingAs(fakeUser($locale)); + + $foo = 'test'; + + getJson(route('via.model.guard', compact('foo', 'locale'))) + ->assertSuccessful() + ->assertJsonPath($foo, LocaleValue::TranslationFrench); + + assertEventDispatched(); +})->with('unknown-locales'); diff --git a/tests/Feature/Middlewares/ModelAuthorizedTest.php b/tests/Feature/Middlewares/ModelAuthorizedTest.php new file mode 100644 index 0000000..a072455 --- /dev/null +++ b/tests/Feature/Middlewares/ModelAuthorizedTest.php @@ -0,0 +1,68 @@ +assertSuccessful() + ->assertJsonPath($foo, LocaleValue::TranslationFrench); + + assertEventNotDispatched(); +}); + +test('aliased locale', function (string $locale) { + actingAs(fakeUser($locale)); + + $foo = 'test'; + + getJson(route('via.model.default', compact('foo', 'locale'))) + ->assertSuccessful() + ->assertJsonPath($foo, LocaleValue::TranslationGerman); + + assertEventDispatched(); +})->with('aliased-locales'); + +test('empty locale', function (int|string|null $locale) { + actingAs(fakeUser($locale)); + + $foo = 'test'; + + getJson(route('via.model.default', compact('foo', 'locale'))) + ->assertSuccessful() + ->assertJsonPath($foo, LocaleValue::TranslationFrench); + + assertEventNotDispatched(); +})->with('empty-locales'); + +test('uninstalled locale', function (string $locale) { + actingAs(fakeUser($locale)); + + $foo = 'test'; + + getJson(route('via.model.default', compact('foo', 'locale'))) + ->assertSuccessful() + ->assertJsonPath($foo, LocaleValue::TranslationFrench); + + assertEventDispatched(); +})->with('uninstalled-locales'); + +test('unknown locale', function (int|string $locale) { + actingAs(fakeUser($locale)); + + $foo = 'test'; + + getJson(route('via.model.default', compact('foo', 'locale'))) + ->assertSuccessful() + ->assertJsonPath($foo, LocaleValue::TranslationFrench); + + assertEventDispatched(); +})->with('unknown-locales'); diff --git a/tests/Feature/Middlewares/ModelUnauthorized.php b/tests/Feature/Middlewares/ModelUnauthorized.php new file mode 100644 index 0000000..935e29b --- /dev/null +++ b/tests/Feature/Middlewares/ModelUnauthorized.php @@ -0,0 +1,27 @@ +assertSuccessful() + ->assertJsonPath($foo, LocaleValue::TranslationFrench); + + assertEventNotDispatched(); +}); + +test('without guard', function () { + $foo = 'test'; + + getJson(route('via.model.default', compact('foo'))) + ->assertSuccessful() + ->assertJsonPath($foo, LocaleValue::TranslationFrench); + + assertEventNotDispatched(); +}); diff --git a/tests/Fixtures/Models/User.php b/tests/Fixtures/Models/User.php new file mode 100644 index 0000000..e1887c6 --- /dev/null +++ b/tests/Fixtures/Models/User.php @@ -0,0 +1,14 @@ + $locale, + ]); +} diff --git a/tests/Pest.php b/tests/Pest.php index 31aebee..aaa1e8f 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,7 +1,8 @@ beforeEach(fn () => Event::fake()) ->in('Feature', 'Unit'); diff --git a/tests/TestCase.php b/tests/TestCase.php index 1a407f4..bf0a85d 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -32,8 +32,18 @@ protected function defineEnvironment($app): void $config->set('app.locale', LocaleValue::LocaleMain); + $config->set('auth.guards.foo', [ + 'driver' => 'session', + 'provider' => 'users', + ]); + $config->set(Name::Shared() . '.aliases', [ LocaleValue::LocaleAliasParent => LocaleValue::LocaleAlias, ]); } + + protected function defineDatabaseMigrations(): void + { + $this->loadMigrationsFrom(__DIR__ . '/database/migrations'); + } } diff --git a/tests/database/migrations/2024_06_20_000000_create_users_table.php b/tests/database/migrations/2024_06_20_000000_create_users_table.php new file mode 100644 index 0000000..2c4a7cf --- /dev/null +++ b/tests/database/migrations/2024_06_20_000000_create_users_table.php @@ -0,0 +1,25 @@ +id(); + + $table->string('locale')->nullable(); + + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('users'); + } +};