Комментарии, файлы и права проекта
- Система комментариев к задачам с вложенными ответами - Редактирование и удаление комментариев - Прикрепление файлов к задачам и комментариям (картинки, архивы до 10 МБ) - Система прав проекта: админ проекта может удалять чужие комментарии и файлы - Универсальный класс FileUpload для загрузки файлов - Защита загрузки: только автор комментария может добавлять файлы - Каскадное удаление: задача → комментарии → файлы - Автообновление комментариев в реальном времени
This commit is contained in:
246
backend/app/class/enity/class_comment.php
Normal file
246
backend/app/class/enity/class_comment.php
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
class Comment extends BaseEntity {
|
||||
|
||||
protected $db_name = 'comments';
|
||||
|
||||
// Свойства комментария
|
||||
public $id;
|
||||
public $id_task;
|
||||
public $id_accounts;
|
||||
public $id_answer; // ID родительского комментария (ответ на)
|
||||
public $text;
|
||||
public $date_create;
|
||||
|
||||
// Создание комментария
|
||||
public function create() {
|
||||
static::$error_message = [];
|
||||
|
||||
// Валидация
|
||||
if (!$this->id_task) {
|
||||
$this->addError('id_task', 'Задача не указана');
|
||||
}
|
||||
if (!$this->id_accounts) {
|
||||
$this->addError('id_accounts', 'Пользователь не указан');
|
||||
}
|
||||
if (!$this->text || trim($this->text) === '') {
|
||||
$this->addError('text', 'Текст комментария не может быть пустым');
|
||||
}
|
||||
if ($errors = $this->getErrors()) {
|
||||
return $errors;
|
||||
}
|
||||
|
||||
// Проверяем что задача существует
|
||||
Task::check_task($this->id_task);
|
||||
|
||||
// Если это ответ — проверяем что родительский комментарий существует
|
||||
if ($this->id_answer) {
|
||||
self::checkComment($this->id_answer);
|
||||
}
|
||||
|
||||
// Вставляем в базу
|
||||
Database::insert($this->db_name, [
|
||||
'id_task' => $this->id_task,
|
||||
'id_accounts' => $this->id_accounts,
|
||||
'id_answer' => $this->id_answer ?: null,
|
||||
'text' => $this->text,
|
||||
'date_create' => date('Y-m-d H:i:s'),
|
||||
'file_img' => '[]'
|
||||
]);
|
||||
|
||||
$this->id = Database::id();
|
||||
|
||||
// Возвращаем созданный комментарий с данными пользователя
|
||||
return [
|
||||
'success' => true,
|
||||
'comment' => $this->getById($this->id)
|
||||
];
|
||||
}
|
||||
|
||||
// Обновление комментария
|
||||
public function update() {
|
||||
static::$error_message = [];
|
||||
|
||||
// Валидация
|
||||
if (!$this->id) {
|
||||
$this->addError('id', 'ID комментария не указан');
|
||||
}
|
||||
if (!$this->text || trim($this->text) === '') {
|
||||
$this->addError('text', 'Текст комментария не может быть пустым');
|
||||
}
|
||||
if ($errors = $this->getErrors()) {
|
||||
return $errors;
|
||||
}
|
||||
|
||||
// Проверяем что комментарий существует
|
||||
$comment = self::checkComment($this->id);
|
||||
|
||||
// Проверяем что пользователь — автор комментария
|
||||
if ((int)$comment['id_accounts'] !== (int)$this->id_accounts) {
|
||||
$this->addError('access', 'Вы можете редактировать только свои комментарии');
|
||||
return $this->getErrors();
|
||||
}
|
||||
|
||||
// Обновляем в БД
|
||||
Database::update($this->db_name, [
|
||||
'text' => $this->text
|
||||
], [
|
||||
'id' => $this->id
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'comment' => $this->getById($this->id)
|
||||
];
|
||||
}
|
||||
|
||||
// Удаление комментария (с дочерними)
|
||||
public static function delete($id, $id_accounts) {
|
||||
// Проверяем что комментарий существует
|
||||
$comment = self::checkComment($id);
|
||||
|
||||
// Получаем задачу для проверки админа проекта
|
||||
$task = Database::get('cards_task', ['id_project'], ['id' => $comment['id_task']]);
|
||||
|
||||
// Проверяем права: автор комментария ИЛИ админ проекта
|
||||
$isAuthor = (int)$comment['id_accounts'] === (int)$id_accounts;
|
||||
$isProjectAdmin = $task && Project::isAdmin($task['id_project'], $id_accounts);
|
||||
|
||||
if (!$isAuthor && !$isProjectAdmin) {
|
||||
RestApi::response([
|
||||
'success' => false,
|
||||
'errors' => ['access' => 'Нет прав на удаление комментария']
|
||||
], 403);
|
||||
}
|
||||
|
||||
// Рекурсивно удаляем все дочерние комментарии
|
||||
self::deleteWithChildren($id);
|
||||
|
||||
return ['success' => true];
|
||||
}
|
||||
|
||||
// Рекурсивное удаление комментария и всех его дочерних
|
||||
private static function deleteWithChildren($id) {
|
||||
// Находим все дочерние комментарии
|
||||
$children = Database::select('comments', ['id'], ['id_answer' => $id]);
|
||||
|
||||
// Рекурсивно удаляем дочерние
|
||||
if ($children) {
|
||||
foreach ($children as $child) {
|
||||
self::deleteWithChildren($child['id']);
|
||||
}
|
||||
}
|
||||
|
||||
// Удаляем папку с файлами комментария
|
||||
FileUpload::deleteFolder('comment', $id);
|
||||
|
||||
// Удаляем сам комментарий
|
||||
Database::delete('comments', ['id' => $id]);
|
||||
}
|
||||
|
||||
// === МЕТОДЫ ДЛЯ РАБОТЫ С ФАЙЛАМИ ===
|
||||
|
||||
// Загрузка файла к комментарию (только автор может загружать)
|
||||
public static function uploadFile($comment_id, $file_base64, $file_name, $user_id) {
|
||||
// Проверка что комментарий существует
|
||||
$comment = self::checkComment($comment_id);
|
||||
|
||||
// Проверка что пользователь — автор комментария
|
||||
if ((int)$comment['id_accounts'] !== (int)$user_id) {
|
||||
RestApi::response([
|
||||
'success' => false,
|
||||
'errors' => ['access' => 'Вы можете загружать файлы только к своим комментариям']
|
||||
], 403);
|
||||
}
|
||||
|
||||
return FileUpload::upload('comment', $comment_id, $file_base64, $file_name);
|
||||
}
|
||||
|
||||
// Удаление файлов комментария (автор или админ проекта)
|
||||
public static function deleteFile($comment_id, $file_names, $user_id) {
|
||||
// Проверка что комментарий существует
|
||||
$comment = self::checkComment($comment_id);
|
||||
|
||||
// Получаем задачу для проверки админа проекта
|
||||
$task = Database::get('cards_task', ['id_project'], ['id' => $comment['id_task']]);
|
||||
|
||||
// Проверка прав: автор комментария ИЛИ админ проекта
|
||||
$isAuthor = (int)$comment['id_accounts'] === (int)$user_id;
|
||||
$isProjectAdmin = $task && Project::isAdmin($task['id_project'], $user_id);
|
||||
|
||||
if (!$isAuthor && !$isProjectAdmin) {
|
||||
RestApi::response([
|
||||
'success' => false,
|
||||
'errors' => ['access' => 'Нет прав на удаление файлов']
|
||||
], 403);
|
||||
}
|
||||
|
||||
return FileUpload::delete('comment', $comment_id, $file_names);
|
||||
}
|
||||
|
||||
// Получение комментария по ID (с данными пользователя)
|
||||
public function getById($id) {
|
||||
$comment = Database::get($this->db_name, [
|
||||
'[>]accounts' => ['id_accounts' => 'id']
|
||||
], [
|
||||
'comments.id',
|
||||
'comments.id_task',
|
||||
'comments.id_accounts',
|
||||
'comments.id_answer',
|
||||
'comments.text',
|
||||
'comments.date_create',
|
||||
'comments.file_img',
|
||||
'accounts.name(author_name)',
|
||||
'accounts.avatar_url(author_avatar)'
|
||||
], [
|
||||
'comments.id' => $id
|
||||
]);
|
||||
|
||||
// Декодируем JSON файлов
|
||||
if ($comment) {
|
||||
$comment['file_img'] = $comment['file_img'] ? json_decode($comment['file_img'], true) : [];
|
||||
}
|
||||
|
||||
return $comment;
|
||||
}
|
||||
|
||||
// Получение всех комментариев задачи
|
||||
public function getByTask($id_task) {
|
||||
// Проверяем что задача существует
|
||||
Task::check_task($id_task);
|
||||
|
||||
$comments = Database::select($this->db_name, [
|
||||
'[>]accounts' => ['id_accounts' => 'id']
|
||||
], [
|
||||
'comments.id',
|
||||
'comments.id_task',
|
||||
'comments.id_accounts',
|
||||
'comments.id_answer',
|
||||
'comments.text',
|
||||
'comments.date_create',
|
||||
'comments.file_img',
|
||||
'accounts.name(author_name)',
|
||||
'accounts.avatar_url(author_avatar)'
|
||||
], [
|
||||
'comments.id_task' => $id_task,
|
||||
'ORDER' => ['comments.date_create' => 'ASC']
|
||||
]);
|
||||
|
||||
// Декодируем JSON файлов для каждого комментария
|
||||
return array_map(function($comment) {
|
||||
$comment['file_img'] = $comment['file_img'] ? json_decode($comment['file_img'], true) : [];
|
||||
return $comment;
|
||||
}, $comments ?: []);
|
||||
}
|
||||
|
||||
// Проверка и получение комментария (при ошибке — сразу ответ и exit)
|
||||
public static function checkComment($id) {
|
||||
$comment = Database::get('comments', '*', ['id' => $id]);
|
||||
if (!$id || !$comment) {
|
||||
RestApi::response(['success' => false, 'errors' => ['comment' => 'Комментарий не найден']], 404);
|
||||
}
|
||||
return $comment;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user