From 241471becea14949ce1258ed739036eecf640745 Mon Sep 17 00:00:00 2001 From: Application-drop-up Date: Thu, 6 Aug 2026 23:32:41 +0900 Subject: [PATCH 1/4] feat: add favorites relation to User model --- src/app/Models/User.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/app/Models/User.php b/src/app/Models/User.php index 741315d8..62c8754c 100644 --- a/src/app/Models/User.php +++ b/src/app/Models/User.php @@ -2,6 +2,7 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Laravel\Sanctum\HasApiTokens; @@ -33,4 +34,14 @@ protected function casts(): array 'email_verified_at' => 'datetime', ]; } + + public function favorites(): BelongsToMany + { + return $this->belongsToMany( + WorldHeritage::class, + 'user_favorite', + 'user_id', + 'world_heritage_site_id' + ); + } } \ No newline at end of file From f2b497d57f2c6899736a3ce5cbe767dc453bc423 Mon Sep 17 00:00:00 2001 From: Application-drop-up Date: Thu, 6 Aug 2026 23:33:54 +0900 Subject: [PATCH 2/4] feat: add FavoriteRepositoryInterface and FavoriteRepository --- .../Domains/Favorite/FavoriteRepository.php | 25 +++++++++++++++++++ .../Interface/FavoriteRepositoryInterface.php | 8 ++++++ 2 files changed, 33 insertions(+) create mode 100644 src/app/Packages/Domains/Favorite/FavoriteRepository.php create mode 100644 src/app/Packages/Domains/Favorite/Interface/FavoriteRepositoryInterface.php diff --git a/src/app/Packages/Domains/Favorite/FavoriteRepository.php b/src/app/Packages/Domains/Favorite/FavoriteRepository.php new file mode 100644 index 00000000..6fa2205d --- /dev/null +++ b/src/app/Packages/Domains/Favorite/FavoriteRepository.php @@ -0,0 +1,25 @@ +userModel->find($userId); + + if ($user === null) { + throw new Exception('User not found.'); + } + + $user->favorites()->attach($worldHeritageSiteId); + } +} diff --git a/src/app/Packages/Domains/Favorite/Interface/FavoriteRepositoryInterface.php b/src/app/Packages/Domains/Favorite/Interface/FavoriteRepositoryInterface.php new file mode 100644 index 00000000..7b1291ac --- /dev/null +++ b/src/app/Packages/Domains/Favorite/Interface/FavoriteRepositoryInterface.php @@ -0,0 +1,8 @@ + Date: Thu, 6 Aug 2026 23:36:38 +0900 Subject: [PATCH 3/4] feat: bind FavoriteRepositoryInterface in RepositoryProvider --- src/app/Providers/RepositoryProvider.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/app/Providers/RepositoryProvider.php b/src/app/Providers/RepositoryProvider.php index cbba05bf..50348344 100644 --- a/src/app/Providers/RepositoryProvider.php +++ b/src/app/Providers/RepositoryProvider.php @@ -2,6 +2,8 @@ namespace App\Providers; +use App\Packages\Domains\Favorite\FavoriteRepository; +use App\Packages\Domains\Favorite\Interface\FavoriteRepositoryInterface; use App\Packages\Domains\User\Interface\UserRepositroyInterface; use App\Packages\Domains\User\UserRepository; use App\Packages\Domains\WorldHeritageRepository; @@ -21,6 +23,11 @@ public function register(): void UserRepositroyInterface::class, UserRepository::class ); + + $this->app->bind( + FavoriteRepositoryInterface::class, + FavoriteRepository::class + ); } public function boot(): void From 7719b6a7f7d054d25b43c58b80d7006caab24ef5 Mon Sep 17 00:00:00 2001 From: Application-drop-up Date: Thu, 6 Aug 2026 23:56:20 +0900 Subject: [PATCH 4/4] test: cover FavoriteRepository addFavorite in integration test --- .../Favorite/Tests/FavoriteRepositoryTest.php | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/app/Packages/Domains/Favorite/Tests/FavoriteRepositoryTest.php diff --git a/src/app/Packages/Domains/Favorite/Tests/FavoriteRepositoryTest.php b/src/app/Packages/Domains/Favorite/Tests/FavoriteRepositoryTest.php new file mode 100644 index 00000000..7b71a092 --- /dev/null +++ b/src/app/Packages/Domains/Favorite/Tests/FavoriteRepositoryTest.php @@ -0,0 +1,93 @@ +truncate(); + } + + protected function tearDown(): void + { + $this->truncate(); + parent::tearDown(); + } + + private function truncate(): void + { + if (env('APP_ENV') === 'testing') { + DB::connection('mysql')->statement('SET FOREIGN_KEY_CHECKS=0;'); + DB::connection('mysql')->table('user_favorite')->truncate(); + WorldHeritage::truncate(); + User::truncate(); + DB::connection('mysql')->statement('SET FOREIGN_KEY_CHECKS=1;'); + } + } + + private function repository(): FavoriteRepository + { + return new FavoriteRepository(new User()); + } + + private function seedUser(): User + { + $faker = FakerFactory::create(); + + return User::create([ + 'first_name' => $faker->firstName(), + 'last_name' => $faker->lastName(), + 'email' => $faker->unique()->safeEmail(), + 'password' => bcrypt('password'), + 'age_range' => 'teens', + 'subscription_tier' => 'free', + 'subscription_expires_at' => null, + ]); + } + + private function seedWorldHeritage(int $id): WorldHeritage + { + return WorldHeritage::create([ + 'id' => $id, + 'official_name' => 'Example Site', + 'name' => 'Example Site', + 'region' => 'Asia', + 'category' => 'Cultural', + 'criteria' => ['i'], + 'year_inscribed' => 2000, + ]); + } + + public function test_addFavorite_attaches_world_heritage_site_to_user(): void + { + $user = $this->seedUser(); + $worldHeritage = $this->seedWorldHeritage(1); + + $this->repository()->addFavorite($user->id, $worldHeritage->id); + + $this->assertDatabaseHas('user_favorite', [ + 'user_id' => $user->id, + 'world_heritage_site_id' => $worldHeritage->id, + ]); + } + + public function test_addFavorite_throws_exception_when_user_not_found(): void + { + $worldHeritage = $this->seedWorldHeritage(1); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('User not found.'); + + $this->repository()->addFavorite(999999, $worldHeritage->id); + } +}