From c4305382cc68ffde589fc16bdc2ee1527130a582 Mon Sep 17 00:00:00 2001 From: Amirmahdi Nourkazemi Date: Mon, 25 Aug 2025 17:58:45 +0330 Subject: [PATCH] feat: breath template & breath session --- .../BreathingExerciseController.php | 91 ++++++++++++++++++- app/Models/BreathingTemplate.php | 20 ++++ app/Models/UserBreathingSession.php | 20 ++++ ...11555_create_breathing_templates_table.php | 33 +++++++ ...5_create_user_breathing_sessions_table.php | 30 ++++++ database/seeders/BreathingTemplateSeeder.php | 29 ++++++ database/seeders/DatabaseSeeder.php | 14 ++- routes/api.php | 12 ++- 8 files changed, 238 insertions(+), 11 deletions(-) create mode 100644 app/Models/BreathingTemplate.php create mode 100644 app/Models/UserBreathingSession.php create mode 100644 database/migrations/2025_08_25_111555_create_breathing_templates_table.php create mode 100644 database/migrations/2025_08_25_111555_create_user_breathing_sessions_table.php create mode 100644 database/seeders/BreathingTemplateSeeder.php diff --git a/app/Http/Controllers/BreathingExerciseController.php b/app/Http/Controllers/BreathingExerciseController.php index 1bb11fb..eed8d7f 100644 --- a/app/Http/Controllers/BreathingExerciseController.php +++ b/app/Http/Controllers/BreathingExerciseController.php @@ -4,10 +4,74 @@ use Illuminate\Http\Request; use App\Models\BreathingExercise; - - +use App\Models\BreathingTemplate; +use App\Models\UserBreathingSession; class BreathingExerciseController extends Controller { + + public function createTemplate(Request $request) + { + $data = $request->validate([ + 'name' => 'required|string', + 'inhale' => 'required|integer|min:1', + 'exhale' => 'required|integer|min:1', + 'repeat' => 'required|integer|min:1', + ]); + + $template = BreathingTemplate::create([ + 'user_id' => auth()->id(), + 'name' => $data['name'], + 'inhale' => $data['inhale'], + 'exhale' => $data['exhale'], + 'repeat' => $data['repeat'], + ]); + + return response()->json(['message' => 'Template created', 'template' => $template]); + } + + public function updateTemplate(Request $request, $id) +{ + $template = BreathingTemplate::where('id', $id) + ->where('user_id', auth()->id()) + ->firstOrFail(); + + $data = $request->validate([ + 'name' => 'sometimes|required|string', + 'inhale' => 'sometimes|required|integer|min:1', + 'exhale' => 'sometimes|required|integer|min:1', + 'repeat' => 'sometimes|required|integer|min:1', + ]); + + $template->update($data); + + return response()->json(['message' => 'Template updated', 'template' => $template]); +} + public function deleteTemplate($id) + { + $template = BreathingTemplate::where('id', $id) + ->where('user_id', auth()->id()) + ->firstOrFail(); + + $template->delete(); + + return response()->json(['message' => 'Template deleted']); + } + + public function getUserTemplates() + { + $templates = BreathingTemplate::where('user_id', auth()->id())->get(); + return response()->json($templates); + } + + + public function getTemplates() + { + $templates = BreathingTemplate::whereNull('user_id') + ->orWhere('user_id', auth()->id()) + ->get(); + + return response()->json($templates); + } public function store(Request $request) { $data = $request->validate([ @@ -37,16 +101,35 @@ public function store(Request $request) ]); } + public function completeSession(Request $request) + { + $data = $request->validate([ + 'template_id' => 'required|exists:breathing_templates,id', + ]); + + $session = UserBreathingSession::create([ + 'user_id' => auth()->id(), + 'template_id' => $data['template_id'], + ]); + + // Increase XP + auth()->user()->increment('xp', 10); + + return response()->json(['message' => 'Session completed', 'session' => $session]); + } + public function profile() { $user = auth()->user(); - $user->load('breathingExercises'); + $templates = BreathingTemplate::where('user_id', $user->id)->get(); + $sessions = UserBreathingSession::with('template')->where('user_id', $user->id)->get(); return response()->json([ 'email' => $user->email, 'name' => $user->first_name . ' ' . $user->last_name, 'xp' => $user->xp, - 'breathing_exercises' => $user->breathingExercises + 'templates' => $templates, + 'sessions' => $sessions ]); } } diff --git a/app/Models/BreathingTemplate.php b/app/Models/BreathingTemplate.php new file mode 100644 index 0000000..324d377 --- /dev/null +++ b/app/Models/BreathingTemplate.php @@ -0,0 +1,20 @@ +belongsTo(User::class); + } + + public function sessions() + { + return $this->hasMany(UserBreathingSession::class, 'template_id'); + } +} diff --git a/app/Models/UserBreathingSession.php b/app/Models/UserBreathingSession.php new file mode 100644 index 0000000..636f4ec --- /dev/null +++ b/app/Models/UserBreathingSession.php @@ -0,0 +1,20 @@ +belongsTo(BreathingTemplate::class, 'template_id'); + } + + public function user() + { + return $this->belongsTo(User::class); + } +} diff --git a/database/migrations/2025_08_25_111555_create_breathing_templates_table.php b/database/migrations/2025_08_25_111555_create_breathing_templates_table.php new file mode 100644 index 0000000..86a739f --- /dev/null +++ b/database/migrations/2025_08_25_111555_create_breathing_templates_table.php @@ -0,0 +1,33 @@ +id(); + $table->foreignId('user_id')->nullable()->constrained()->onDelete('cascade'); // null = default template + $table->string('name'); + $table->integer('inhale'); + $table->integer('exhale'); + $table->integer('repeat'); + $table->timestamps(); + }); +} + + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('breathing_templates'); + } +}; diff --git a/database/migrations/2025_08_25_111555_create_user_breathing_sessions_table.php b/database/migrations/2025_08_25_111555_create_user_breathing_sessions_table.php new file mode 100644 index 0000000..693e17d --- /dev/null +++ b/database/migrations/2025_08_25_111555_create_user_breathing_sessions_table.php @@ -0,0 +1,30 @@ +id(); + $table->foreignId('user_id')->constrained()->onDelete('cascade'); + $table->foreignId('template_id')->constrained('breathing_templates')->onDelete('cascade'); + $table->timestamps(); + }); +} + + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('user_breathing_sessions'); + } +}; diff --git a/database/seeders/BreathingTemplateSeeder.php b/database/seeders/BreathingTemplateSeeder.php new file mode 100644 index 0000000..d323d3b --- /dev/null +++ b/database/seeders/BreathingTemplateSeeder.php @@ -0,0 +1,29 @@ + 'نفس آرام', 'inhale' => 4, 'exhale' => 4, 'repeat' => 5], + ['name' => 'نفس تمرکزی', 'inhale' => 3, 'exhale' => 3, 'repeat' => 8], + ['name' => 'نفس تسکین دهنده', 'inhale' => 6, 'exhale' => 6, 'repeat' => 10], + ['name' => 'نفس انرژی بخش', 'inhale' => 2, 'exhale' => 2, 'repeat' => 12], + ['name' => 'نفس عمیق', 'inhale' => 5, 'exhale' => 5, 'repeat' => 7], + ['name' => 'نفس آرامش بخش', 'inhale' => 4, 'exhale' => 6, 'repeat' => 6], + ['name' => 'نفس پاکسازی', 'inhale' => 3, 'exhale' => 5, 'repeat' => 8], + ['name' => 'نفس ذهنی', 'inhale' => 4, 'exhale' => 4, 'repeat' => 9], + ['name' => 'نفس کنترل استرس', 'inhale' => 5, 'exhale' => 7, 'repeat' => 6], + ['name' => 'نفس تمرینی', 'inhale' => 3, 'exhale' => 4, 'repeat' => 10], + ]; + + foreach ($templates as $template) { + BreathingTemplate::create($template); + } + } +} diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index d01a0ef..7eff214 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -8,16 +8,20 @@ class DatabaseSeeder extends Seeder { + /** * Seed the application's database. */ public function run(): void { // User::factory(10)->create(); - - User::factory()->create([ - 'name' => 'Test User', - 'email' => 'test@example.com', - ]); +$this->call([ + BreathingTemplateSeeder::class, +]); + // User::factory()->create([ + // 'name' => 'Test User', + // 'email' => 'test@example.com', + // ]); } + } diff --git a/routes/api.php b/routes/api.php index 2b2402c..d3f99ee 100644 --- a/routes/api.php +++ b/routes/api.php @@ -6,7 +6,7 @@ use App\Http\Controllers\BreathingExerciseController; use App\Http\Controllers\PackageNameController; use App\Http\Controllers\ProductController; - +use App\Http\Controllers\BreathingTemplate; Route::get('package-names/{name}/products', [ProductController::class, 'index']); Route::prefix('auth')->controller(UserController::class)->group(function () { @@ -39,5 +39,13 @@ Route::post('breathing-exercises', [BreathingExerciseController::class, 'store']); Route::get('profile', [BreathingExerciseController::class, 'profile']); - + + // ------------------------------------------------ + Route::post('breathing-templates', [BreathingExerciseController::class, 'createTemplate']); + Route::get('breathing-templates', [BreathingExerciseController::class, 'getTemplates']); + Route::post('breathing-complete', [BreathingExerciseController::class, 'completeSession']); + Route::get('profile', [BreathingExerciseController::class, 'profile']); + Route::put('breathing-templates/{id}', [BreathingExerciseController::class, 'updateTemplate']); + Route::delete('breathing-templates/{id}', [BreathingExerciseController::class, 'deleteTemplate']); + Route::get('user-templates', [BreathingExerciseController::class, 'getUserTemplates']); }); \ No newline at end of file