add projet

This commit is contained in:
root
2026-03-05 12:29:37 -05:00
parent 8d1eef8ba8
commit 23b7db1107
9109 changed files with 1106501 additions and 73 deletions
+413
View File
@@ -0,0 +1,413 @@
<?php
namespace App\Http\Controllers\Api;
use App\Models\Configuration;
use App\Models\Message;
use App\Models\User;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpFoundation\Response;
class MessagingController extends BaseApiController
{
protected Message $message;
protected User $user;
protected Configuration $config;
public function __construct()
{
parent::__construct();
$this->message = model(Message::class);
$this->user = model(User::class);
$this->config = model(Configuration::class);
}
public function conversations()
{
$user = $this->getCurrentUser();
if (!$user) {
return $this->respondError('Authentication required', Response::HTTP_UNAUTHORIZED);
}
$userId = (int) $user->id;
$sql = <<<'SQL'
SELECT
CASE WHEN sender_id = ? THEN recipient_id ELSE sender_id END AS other_user_id,
MAX(sent_datetime) AS last_message_time,
SUM(CASE WHEN recipient_id = ? AND read_status = 0 THEN 1 ELSE 0 END) AS unread_count,
(
SELECT m2.message
FROM messages m2
WHERE (m2.sender_id = ? AND m2.recipient_id = CASE WHEN m1.sender_id = ? THEN m1.recipient_id ELSE m1.sender_id END)
OR (m2.recipient_id = ? AND m2.sender_id = CASE WHEN m1.sender_id = ? THEN m1.recipient_id ELSE m1.sender_id END)
ORDER BY m2.sent_datetime DESC
LIMIT 1
) AS last_message
FROM messages m1
WHERE (sender_id = ? OR recipient_id = ?)
AND (subject = '' OR subject IS NULL OR subject = 'chat')
GROUP BY other_user_id
ORDER BY last_message_time DESC
SQL;
$rows = DB::select($sql, array_fill(0, 8, $userId));
$conversations = [];
foreach ($rows as $row) {
$otherUser = $this->user->find($row->other_user_id);
if (!$otherUser) {
continue;
}
$conversations[] = [
'user_id' => $row->other_user_id,
'user_name' => trim(($otherUser['firstname'] ?? '') . ' ' . ($otherUser['lastname'] ?? '')),
'user_initials' => strtoupper(($otherUser['firstname'][0] ?? '') . ($otherUser['lastname'][0] ?? '')),
'last_message' => $row->last_message ?? '',
'last_message_time'=> $row->last_message_time ?? null,
'unread_count' => (int) ($row->unread_count ?? 0),
];
}
return $this->success($conversations, 'Conversations retrieved successfully');
}
public function conversation($userId = null)
{
$user = $this->getCurrentUser();
if (!$user) {
return $this->respondError('Authentication required', Response::HTTP_UNAUTHORIZED);
}
if (!$userId) {
return $this->error('User ID is required', Response::HTTP_BAD_REQUEST);
}
$currentUserId = (int) $user->id;
$otherUserId = (int) $userId;
$otherUser = $this->user->find($otherUserId);
if (!$otherUser) {
return $this->error('User not found', Response::HTTP_NOT_FOUND);
}
$page = max(1, (int) ($this->request->getGet('page') ?? 1));
$perPage = min(100, max(1, (int) ($this->request->getGet('per_page') ?? 50)));
$offset = ($page - 1) * $perPage;
$baseQuery = DB::table('messages')
->where(function ($query) use ($currentUserId, $otherUserId) {
$query->where('sender_id', $currentUserId)
->where('recipient_id', $otherUserId);
})
->orWhere(function ($query) use ($currentUserId, $otherUserId) {
$query->where('sender_id', $otherUserId)
->where('recipient_id', $currentUserId);
})
->whereRaw("(subject = '' OR subject IS NULL OR subject = 'chat')")
->orderBy('sent_datetime', 'ASC');
$total = (clone $baseQuery)->count();
$messages = (clone $baseQuery)->offset($offset)->limit($perPage)->get();
foreach ($messages as $message) {
if ($message->recipient_id === $currentUserId && ((int) $message->read_status) === 0) {
$this->message->update($message->id, [
'read_status' => 1,
'read_datetime'=> Carbon::now('UTC')->format('Y-m-d H:i:s'),
]);
}
}
$enriched = [];
foreach ($messages as $msg) {
$enriched[] = [
'id' => $msg->id,
'sender_id' => $msg->sender_id,
'recipient_id' => $msg->recipient_id,
'message' => $msg->message,
'sent_datetime' => $msg->sent_datetime,
'read_status' => $msg->read_status,
'read_datetime' => $msg->read_datetime,
'attachment' => $msg->attachment ?? null,
'is_sender' => $msg->sender_id === $currentUserId,
];
}
$payload = [
'messages' => $enriched,
'other_user' => [
'id' => $otherUser['id'],
'name' => trim(($otherUser['firstname'] ?? '') . ' ' . ($otherUser['lastname'] ?? '')),
'initials' => strtoupper(($otherUser['firstname'][0] ?? '') . ($otherUser['lastname'][0] ?? '')),
],
'pagination' => [
'current_page' => $page,
'per_page' => $perPage,
'total' => $total,
'total_pages' => (int) ceil($total / $perPage),
],
];
return $this->success($payload, 'Messages retrieved successfully');
}
public function send()
{
$user = $this->getCurrentUser();
if (!$user) {
return $this->respondError('Authentication required', Response::HTTP_UNAUTHORIZED);
}
$payload = $this->payloadData();
if (empty($payload)) {
return $this->error('Invalid request data', Response::HTTP_BAD_REQUEST);
}
$rules = [
'recipient_id' => 'required|integer',
'message' => 'required|max_length[5000]',
];
$errors = $this->validateRequest($payload, $rules);
if (!empty($errors)) {
return $this->respondValidationError($errors);
}
$recipientId = (int) $payload['recipient_id'];
if ($recipientId === (int) $user->id) {
return $this->error('Cannot send message to yourself', Response::HTTP_BAD_REQUEST);
}
$recipient = $this->user->find($recipientId);
if (!$recipient) {
return $this->error('Recipient not found', Response::HTTP_NOT_FOUND);
}
$schoolYear = $this->config->getConfig('school_year') ?? date('Y');
$semester = $this->config->getConfig('semester') ?? 'Fall';
$maxMessageNumber = $this->message->newQuery()
->selectRaw('COALESCE(MAX(message_number), 0) AS message_number')
->first();
$messageNumber = (int) ($maxMessageNumber->message_number ?? $maxMessageNumber['message_number'] ?? 0) + 1;
$messageData = [
'sender_id' => (int) $user->id,
'recipient_id' => $recipientId,
'subject' => 'chat',
'message' => $payload['message'],
'sent_datetime' => Carbon::now('UTC')->format('Y-m-d H:i:s'),
'read_status' => 0,
'message_number'=> $messageNumber,
'priority' => 'normal',
'status' => 'sent',
'semester' => $semester,
'school_year' => $schoolYear,
'attachment' => $payload['attachment'] ?? null,
];
try {
$message = $this->message->create($messageData);
return $this->success([
'id' => $message->id,
'sender_id' => $message->sender_id,
'recipient_id' => $message->recipient_id,
'message' => $message->message,
'sent_datetime' => $message->sent_datetime,
'read_status' => $message->read_status,
'attachment' => $message->attachment,
'is_sender' => true,
], 'Message sent successfully', Response::HTTP_CREATED);
} catch (\Throwable $e) {
log_message('error', 'Message send error: ' . $e->getMessage());
return $this->respondError('Failed to send message', Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
public function markRead()
{
$user = $this->getCurrentUser();
if (!$user) {
return $this->respondError('Authentication required', Response::HTTP_UNAUTHORIZED);
}
$payload = $this->payloadData();
if (empty($payload)) {
return $this->error('Invalid request data', Response::HTTP_BAD_REQUEST);
}
$currentUserId = (int) $user->id;
try {
if (!empty($payload['message_ids']) && is_array($payload['message_ids'])) {
$messageIds = array_map('intval', $payload['message_ids']);
$updated = $this->message->newQuery()
->whereIn('id', $messageIds)
->where('recipient_id', $currentUserId)
->where('read_status', 0)
->update([
'read_status' => 1,
'read_datetime'=> Carbon::now('UTC')->format('Y-m-d H:i:s'),
]);
return $this->success([
'updated_count' => $updated,
], 'Messages marked as read');
}
if (isset($payload['user_id'])) {
$senderId = (int) $payload['user_id'];
$updated = $this->message->newQuery()
->where('sender_id', $senderId)
->where('recipient_id', $currentUserId)
->where('read_status', 0)
->update([
'read_status' => 1,
'read_datetime'=> Carbon::now('UTC')->format('Y-m-d H:i:s'),
]);
return $this->success([
'updated_count' => $updated,
], 'Messages marked as read');
}
return $this->error('Either message_ids or user_id is required', Response::HTTP_BAD_REQUEST);
} catch (\Throwable $e) {
log_message('error', 'Mark read error: ' . $e->getMessage());
return $this->respondError('Failed to mark messages as read', Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
public function unreadCount()
{
$user = $this->getCurrentUser();
if (!$user) {
return $this->respondError('Authentication required', Response::HTTP_UNAUTHORIZED);
}
try {
$count = $this->message->newQuery()
->where('recipient_id', (int) $user->id)
->where('read_status', 0)
->whereRaw("(subject = '' OR subject IS NULL OR subject = 'chat')")
->count();
return $this->success([
'unread_count' => $count,
], 'Unread count retrieved successfully');
} catch (\Throwable $e) {
log_message('error', 'Unread count error: ' . $e->getMessage());
return $this->respondError('Failed to get unread count', Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
public function searchUsers()
{
$user = $this->getCurrentUser();
if (!$user) {
return $this->respondError('Authentication required', Response::HTTP_UNAUTHORIZED);
}
$query = trim((string) ($this->request->getGet('q') ?? ''));
if (strlen($query) < 2) {
return $this->error('Search query must be at least 2 characters', Response::HTTP_BAD_REQUEST);
}
$limit = min(20, max(1, (int) ($this->request->getGet('limit') ?? 10)));
try {
$users = $this->user->newQuery()
->where(function ($q) use ($query) {
$q->where('firstname', 'LIKE', "%{$query}%")
->orWhere('lastname', 'LIKE', "%{$query}%")
->orWhere('email', 'LIKE', "%{$query}%");
})
->where('id', '!=', (int) $user->id)
->where('status', 'active')
->limit($limit)
->get();
$results = [];
foreach ($users as $u) {
$results[] = [
'id' => $u->id,
'name' => trim(($u->firstname ?? '') . ' ' . ($u->lastname ?? '')),
'initials' => strtoupper(($u->firstname[0] ?? '') . ($u->lastname[0] ?? '')),
'email' => $u->email ?? '',
];
}
return $this->success($results, 'Users retrieved successfully');
} catch (\Throwable $e) {
log_message('error', 'Search users error: ' . $e->getMessage());
return $this->respondError('Failed to search users', Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
public function deleteMessage($id = null)
{
$user = $this->getCurrentUser();
if (!$user) {
return $this->respondError('Authentication required', Response::HTTP_UNAUTHORIZED);
}
if (!$id) {
return $this->error('Message ID is required', Response::HTTP_BAD_REQUEST);
}
$message = $this->message->find($id);
if (!$message) {
return $this->error('Message not found', Response::HTTP_NOT_FOUND);
}
if ($message->sender_id !== (int) $user->id && $message->recipient_id !== (int) $user->id) {
return $this->error('Unauthorized to delete this message', Response::HTTP_FORBIDDEN);
}
$this->message->update($id, [
'status' => 'deleted',
]);
return $this->success(null, 'Message deleted successfully');
}
public function deleteConversation($userId = null)
{
$user = $this->getCurrentUser();
if (!$user) {
return $this->respondError('Authentication required', Response::HTTP_UNAUTHORIZED);
}
if (!$userId) {
return $this->error('User ID is required', Response::HTTP_BAD_REQUEST);
}
$currentUserId = (int) $user->id;
$otherUserId = (int) $userId;
try {
$updated = $this->message->newQuery()
->where(function ($query) use ($currentUserId, $otherUserId) {
$query->where('sender_id', $currentUserId)
->where('recipient_id', $otherUserId);
})
->orWhere(function ($query) use ($currentUserId, $otherUserId) {
$query->where('sender_id', $otherUserId)
->where('recipient_id', $currentUserId);
})
->whereRaw("(subject = '' OR subject IS NULL OR subject = 'chat')")
->update(['status' => 'deleted']);
return $this->success([
'deleted_count' => $updated,
], 'Conversation deleted successfully');
} catch (\Throwable $e) {
log_message('error', 'Delete conversation error: ' . $e->getMessage());
return $this->respondError('Failed to delete conversation', Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
}