Files
tehnobox/app/Http/Controllers/Admin/ChatController.php
ssww23 93a655235a
Some checks failed
Deploy / deploy (push) Has been cancelled
Initial commit
2026-03-10 00:55:37 +03:00

179 lines
5.9 KiB
PHP

<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\ChatConversation;
use App\Models\ChatMessage;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Illuminate\View\View;
class ChatController extends Controller
{
public function index(Request $request): View
{
$conversations = ChatConversation::query()
->with('user', 'latestMessage')
->withCount([
'messages as unread_count' => fn ($query) => $query
->where('sender', 'customer')
->where('is_read', false),
])
->orderByDesc('last_message_at')
->orderByDesc('id')
->paginate(30);
$selectedConversation = $this->resolveSelectedConversation($request, $conversations);
$initialMessages = collect();
if ($selectedConversation) {
$this->markCustomerMessagesAsRead($selectedConversation);
$initialMessages = $this->conversationMessages($selectedConversation);
}
return view('admin.chats.index', [
'conversations' => $conversations,
'selectedConversation' => $selectedConversation,
'initialMessages' => $initialMessages->map(fn (ChatMessage $message) => $this->messagePayload($message))->values(),
]);
}
public function messages(ChatConversation $conversation): JsonResponse
{
$this->markCustomerMessagesAsRead($conversation);
return response()->json([
'conversation' => $this->conversationPayload($conversation->loadMissing('user')),
'messages' => $this->conversationMessages($conversation)
->map(fn (ChatMessage $message) => $this->messagePayload($message))
->values(),
]);
}
public function storeMessage(Request $request, ChatConversation $conversation): JsonResponse
{
$validated = $request->validate([
'message' => ['required', 'string', 'max:2000'],
]);
$messageText = $this->sanitizeMessage((string) $validated['message']);
if ($messageText === '') {
throw ValidationException::withMessages([
'message' => 'Сообщение содержит недопустимые символы.',
]);
}
$message = $conversation->messages()->create([
'sender' => 'admin',
'body' => $messageText,
'is_read' => false,
]);
$conversation->update([
'status' => 'open',
'last_message_at' => now(),
]);
return response()->json([
'message' => $this->messagePayload($message),
]);
}
public function updateStatus(Request $request, ChatConversation $conversation): RedirectResponse
{
$validated = $request->validate([
'status' => ['required', 'string', Rule::in([
ChatConversation::STATUS_OPEN,
ChatConversation::STATUS_CLOSED,
])],
]);
$conversation->update([
'status' => $validated['status'],
]);
return redirect()
->route('admin.chats.index', ['conversation' => $conversation->id])
->with('status', $conversation->isClosed() ? 'Чат закрыт.' : 'Чат открыт.');
}
public function destroy(ChatConversation $conversation): RedirectResponse
{
$conversation->delete();
return redirect()
->route('admin.chats.index')
->with('status', 'Чат удален.');
}
private function resolveSelectedConversation(Request $request, $conversations): ?ChatConversation
{
$selectedId = $request->integer('conversation');
if ($selectedId > 0) {
return ChatConversation::query()->with('user')->find($selectedId);
}
$firstConversation = $conversations->first();
if (!$firstConversation) {
return null;
}
return ChatConversation::query()->with('user')->find($firstConversation->id);
}
private function markCustomerMessagesAsRead(ChatConversation $conversation): void
{
$conversation->messages()
->where('sender', 'customer')
->where('is_read', false)
->update(['is_read' => true]);
}
private function conversationMessages(ChatConversation $conversation)
{
return $conversation->messages()
->orderBy('id')
->limit(200)
->get();
}
private function messagePayload(ChatMessage $message): array
{
return [
'id' => $message->id,
'sender' => $message->sender,
'body' => $message->body,
'created_at' => $message->created_at?->toIso8601String(),
'time' => $message->created_at?->format('H:i'),
];
}
private function conversationPayload(ChatConversation $conversation): array
{
$preview = $conversation->latestMessage?->body;
return [
'id' => $conversation->id,
'title' => $conversation->display_name,
'subtitle' => $conversation->user?->email ?: ('Токен: ' . Str::limit($conversation->visitor_token, 14, '...')),
'preview' => $preview ? Str::limit($preview, 80) : 'Нет сообщений',
'status' => $conversation->status,
'is_closed' => $conversation->isClosed(),
];
}
private function sanitizeMessage(string $value): string
{
$text = strip_tags($value);
$text = str_replace(["\r\n", "\r"], "\n", $text);
$text = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $text) ?? $text;
return trim(Str::limit($text, 2000, ''));
}
}