From 005b428cf1d1d3aab8d90eb6b4f07db7a3558d2f Mon Sep 17 00:00:00 2001 From: Mekan1206 Date: Fri, 1 May 2026 16:53:04 +0500 Subject: [PATCH] Channel --- app/Events/Conversation/MessageSent.php | 37 +++++++++++ .../V1/Collection/CollectionController.php | 4 +- .../Api/V1/Filters/FilterController.php | 13 ++++ .../Filters/Requests/FilterIndexRequest.php | 1 + app/Http/Controllers/ChatController.php | 65 +++++++++++++++++++ app/Models/Chat/Conversation.php | 22 +++++++ app/Models/Chat/Message.php | 17 +++++ ...4_30_200129_create_conversations_table.php | 27 ++++++++ ..._200155_create_conversation_user_table.php | 31 +++++++++ ...026_04_30_200220_create_messages_table.php | 31 +++++++++ routes/api/v1/v1-api.php | 7 ++ routes/channels.php | 8 +++ 12 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 app/Events/Conversation/MessageSent.php create mode 100644 app/Http/Controllers/ChatController.php create mode 100644 app/Models/Chat/Conversation.php create mode 100644 app/Models/Chat/Message.php create mode 100644 database/migrations/2026_04_30_200129_create_conversations_table.php create mode 100644 database/migrations/2026_04_30_200155_create_conversation_user_table.php create mode 100644 database/migrations/2026_04_30_200220_create_messages_table.php diff --git a/app/Events/Conversation/MessageSent.php b/app/Events/Conversation/MessageSent.php new file mode 100644 index 0000000..ec82a79 --- /dev/null +++ b/app/Events/Conversation/MessageSent.php @@ -0,0 +1,37 @@ +message = $message->load('user'); + } + + /** + * Get the channels the event should broadcast on. + * + * @return array + */ + public function broadcastOn(): array + { + return [ + new PrivateChannel('chat.'.$this->message->conversation_id), + ]; + } +} diff --git a/app/Http/Controllers/Api/V1/Collection/CollectionController.php b/app/Http/Controllers/Api/V1/Collection/CollectionController.php index dbd3410..6152baa 100644 --- a/app/Http/Controllers/Api/V1/Collection/CollectionController.php +++ b/app/Http/Controllers/Api/V1/Collection/CollectionController.php @@ -20,7 +20,7 @@ class CollectionController extends Controller { return response()->rest( CollectionResource::collection( - Collection::with('media')->where('is_visible', true)->ordered()->get() + Collection::query()->with('media')->where('is_visible', true)->inRandomOrder()->get() ) ); } @@ -34,7 +34,7 @@ class CollectionController extends Controller return response()->rest_paginate( CollectionResource::collection( - Collection::with('media')->where('is_visible', true)->ordered()->simplePaginate($perPage) + Collection::query()->with('media')->where('is_visible', true)->inRandomOrder()->simplePaginate($perPage) ) ); } diff --git a/app/Http/Controllers/Api/V1/Filters/FilterController.php b/app/Http/Controllers/Api/V1/Filters/FilterController.php index 1309b43..7000ab7 100644 --- a/app/Http/Controllers/Api/V1/Filters/FilterController.php +++ b/app/Http/Controllers/Api/V1/Filters/FilterController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Filters; use App\Http\Controllers\Api\V1\Filters\Requests\FilterIndexRequest; use App\Http\Controllers\Controller; +use App\Models\Ecommerce\Channel\Channel; use App\Models\Ecommerce\Product\Brand\Brand; use App\Models\Ecommerce\Product\Category\Category; use App\Models\Ecommerce\Product\Collection\Collection; @@ -51,6 +52,10 @@ class FilterController extends Controller return $this->filterByCategoryResource(Brand::find($this->request->brand_id)); } + if ($this->shouldFilterByChannel()) { + return $this->filterByCategoryResource(Channel::find($this->request->channel_id)); + } + return Category::query()->where('is_visible', true)->ordered()->get(['id', 'parent_id', 'name']); } @@ -127,4 +132,12 @@ class FilterController extends Controller { return $this->request->filled('brand_id'); } + + /** + * Check if request should be filtered by channel + */ + private function shouldFilterByChannel(): bool + { + return $this->request->filled('channel_id'); + } } diff --git a/app/Http/Controllers/Api/V1/Filters/Requests/FilterIndexRequest.php b/app/Http/Controllers/Api/V1/Filters/Requests/FilterIndexRequest.php index 013bfc0..a25e674 100644 --- a/app/Http/Controllers/Api/V1/Filters/Requests/FilterIndexRequest.php +++ b/app/Http/Controllers/Api/V1/Filters/Requests/FilterIndexRequest.php @@ -18,6 +18,7 @@ class FilterIndexRequest extends FormRequest 'collection_id' => ['bail', 'nullable', 'int', 'exists:collections,id'], 'category_id' => ['bail', 'nullable', 'int', 'exists:categories,id'], 'brand_id' => ['bail', 'nullable', 'int', 'exists:brands,id'], + 'channel_id' => ['bail', 'nullable', 'int', 'exists:channels,id'], ]; } } diff --git a/app/Http/Controllers/ChatController.php b/app/Http/Controllers/ChatController.php new file mode 100644 index 0000000..6d3138d --- /dev/null +++ b/app/Http/Controllers/ChatController.php @@ -0,0 +1,65 @@ +get() + ->map(function ($user) { + return [ + 'id' => $user->id, + 'name' => $user->first_name.' '.$user->last_name, + ]; + }); + } + + public function start(Request $request) + { + $user1 = auth()->id(); + $user2 = $request->user_id; + + $conversation = Conversation::whereHas('users', fn ($q) => $q->where('user_id', $user1)) + ->whereHas('users', fn ($q) => $q->where('user_id', $user2)) + ->first(); + + if (! $conversation) { + $conversation = Conversation::create(); + $conversation->users()->attach([$user1, $user2]); + } + + return $conversation; + } + + public function messages($id) + { + return Message::with('user') + ->where('conversation_id', $id) + ->latest() + ->take(50) + ->get() + ->reverse() + ->values(); + } + + public function send(Request $request) + { + $message = Message::create([ + 'conversation_id' => $request->conversation_id, + 'user_id' => auth()->id(), + 'body' => $request->body, + ]); + + broadcast(new MessageSent($message))->toOthers(); + + return $message->load('user'); + } +} diff --git a/app/Models/Chat/Conversation.php b/app/Models/Chat/Conversation.php new file mode 100644 index 0000000..825cf0b --- /dev/null +++ b/app/Models/Chat/Conversation.php @@ -0,0 +1,22 @@ +belongsToMany(User::class); + } + + public function messages() + { + return $this->hasMany(Message::class); + } +} diff --git a/app/Models/Chat/Message.php b/app/Models/Chat/Message.php new file mode 100644 index 0000000..2a07be5 --- /dev/null +++ b/app/Models/Chat/Message.php @@ -0,0 +1,17 @@ +belongsTo(User::class); + } +} diff --git a/database/migrations/2026_04_30_200129_create_conversations_table.php b/database/migrations/2026_04_30_200129_create_conversations_table.php new file mode 100644 index 0000000..76c20ef --- /dev/null +++ b/database/migrations/2026_04_30_200129_create_conversations_table.php @@ -0,0 +1,27 @@ +id(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('conversations'); + } +}; diff --git a/database/migrations/2026_04_30_200155_create_conversation_user_table.php b/database/migrations/2026_04_30_200155_create_conversation_user_table.php new file mode 100644 index 0000000..e82d056 --- /dev/null +++ b/database/migrations/2026_04_30_200155_create_conversation_user_table.php @@ -0,0 +1,31 @@ +id(); + $table->foreignId('conversation_id')->constrained()->cascadeOnDelete(); + $table->foreignId('user_id')->constrained()->cascadeOnDelete(); + $table->timestamps(); + + $table->unique(['conversation_id', 'user_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('conversation_user'); + } +}; diff --git a/database/migrations/2026_04_30_200220_create_messages_table.php b/database/migrations/2026_04_30_200220_create_messages_table.php new file mode 100644 index 0000000..537838e --- /dev/null +++ b/database/migrations/2026_04_30_200220_create_messages_table.php @@ -0,0 +1,31 @@ +id(); + $table->foreignId('conversation_id')->constrained()->cascadeOnDelete(); + $table->foreignId('user_id')->constrained()->cascadeOnDelete(); + $table->text('body'); + $table->timestamp('seen_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('messages'); + } +}; diff --git a/routes/api/v1/v1-api.php b/routes/api/v1/v1-api.php index d206cfe..d6e1d73 100644 --- a/routes/api/v1/v1-api.php +++ b/routes/api/v1/v1-api.php @@ -101,6 +101,13 @@ Route::get('filters', [FilterController::class, 'index']); // Global orders... Route::post('global-order', [GlobalOrderController::class, 'store']); +Route::middleware('auth:sanctum')->group(function () { + Route::get('/chat/contacts', [ChatController::class, 'contacts']); + Route::get('/chat/messages/{conversation}', [ChatController::class, 'messages']); + Route::post('/chat/start', [ChatController::class, 'start']); + Route::post('/chat/send', [ChatController::class, 'send']); +}); + Route::middleware(['auth:sanctum', 'banned'])->group(function () { // Profile... Route::get('profile', [ProfileController::class, 'index']); diff --git a/routes/channels.php b/routes/channels.php index 409c335..fa3421a 100644 --- a/routes/channels.php +++ b/routes/channels.php @@ -1,6 +1,7 @@ where('conversation_id', $conversationId) + ->where('user_id', $user->id) + ->exists(); +});