This commit is contained in:
Mekan1206
2026-05-01 16:53:04 +05:00
parent f772562376
commit 005b428cf1
12 changed files with 261 additions and 2 deletions

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Events\Conversation;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class MessageSent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
/**
* Create a new event instance.
*/
public function __construct($message)
{
$this->message = $message->load('user');
}
/**
* Get the channels the event should broadcast on.
*
* @return array<int, Channel>
*/
public function broadcastOn(): array
{
return [
new PrivateChannel('chat.'.$this->message->conversation_id),
];
}
}

View File

@@ -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)
)
);
}

View File

@@ -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');
}
}

View File

@@ -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'],
];
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers;
use App\Events\Conversation\MessageSent;
use App\Models\Chat\Conversation;
use App\Models\Chat\Message;
use App\Models\User;
use Illuminate\Http\Request;
class ChatController extends Controller
{
public function contacts()
{
return User::select(['id', 'first_name', 'last_name'])
->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');
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Models\Chat;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Conversation extends Model
{
use HasFactory;
public function users()
{
return $this->belongsToMany(User::class);
}
public function messages()
{
return $this->hasMany(Message::class);
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Models\Chat;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Message extends Model
{
use HasFactory;
public function user()
{
return $this->belongsTo(User::class);
}
}

View File

@@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('conversations', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('conversations');
}
};

View File

@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('conversation_user', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('messages', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -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']);

View File

@@ -1,6 +1,7 @@
<?php
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\Facades\DB;
/*
|--------------------------------------------------------------------------
@@ -12,3 +13,10 @@ use Illuminate\Support\Facades\Broadcast;
| used to check if an authenticated user can listen to the channel.
|
*/
Broadcast::channel('chat.{conversationId}', function ($user, $conversationId) {
return DB::table('conversation_user')
->where('conversation_id', $conversationId)
->where('user_id', $user->id)
->exists();
});