id) { $this->addError('id', 'ID задачи не указан'); } if (!$this->title) { $this->addError('title', 'Название не может быть пустым'); } return $this->getErrors(); } // Валидация данных (для create) protected function validateCreate() { static::$error_message = []; if (!$this->title) { $this->addError('title', 'Название не может быть пустым'); } if (!$this->column_id) { $this->addError('column_id', 'Колонка не указана'); } if (!$this->id_department) { $this->addError('id_department', 'Департамент не указан'); } if (!$this->id_project) { $this->addError('id_project', 'Проект не указан'); } return $this->getErrors(); } // Создание задачи (с файлами) public function create($files = []) { // Валидация if ($errors = $this->validateCreate()) { return $errors; } // Вставляем в базу Database::insert($this->db_name, [ 'id_project' => $this->id_project, 'id_department' => $this->id_department, 'id_label' => $this->id_label, 'order' => $this->order ?? 0, 'column_id' => $this->column_id, 'date' => $this->date ?: null, 'id_account' => $this->id_account, 'title' => $this->title, 'descript' => $this->descript ?: null, 'descript_full' => $this->descript_full ?: null, 'archive' => 0, 'date_create' => date('Y-m-d H:i:s'), 'file_img' => '[]' ]); // Получаем ID созданной задачи $this->id = Database::id(); // Загружаем файлы если есть $uploaded_files = []; if (!empty($files)) { foreach ($files as $file) { $result = FileUpload::upload('task', $this->id, $file['data'], $file['name']); if ($result['success']) { $uploaded_files[] = $result['file']; } } } return [ 'success' => true, 'id' => $this->id, 'files' => $uploaded_files ]; } // Обновление задачи public function update() { // Валидация if ($errors = $this->validate()) { return $errors; } // Проверка что задача существует и получаем текущие данные $task = Database::get($this->db_name, ['id', 'column_id', 'order', 'id_project'], ['id' => $this->id]); if (!$task) { $this->addError('task', 'Задача не найдена'); return $this->getErrors(); } // Получаем текущую колонку $old_column_id = (int)$task['column_id']; // Если column_id не передан — оставляем текущий $new_column_id = $this->column_id !== null ? (int)$this->column_id : $old_column_id; // Получаем id_ready (колонка "Готово") из проекта $done_column_id = Project::getReadyColumnId($task['id_project']); // Формируем данные для обновления $update_data = [ 'id_department' => $this->id_department, 'id_label' => $this->id_label, 'order' => $this->order ?? $task['order'], 'column_id' => $new_column_id, 'date' => $this->date ?: null, 'id_account' => $this->id_account, 'title' => $this->title, 'descript' => $this->descript ?: null, 'descript_full' => $this->descript_full ?: null ]; // Обновляем date_closed при смене колонки if ($done_column_id && $new_column_id === $done_column_id && $old_column_id !== $done_column_id) { $update_data['date_closed'] = date('Y-m-d H:i:s'); } elseif ($done_column_id && $old_column_id === $done_column_id && $new_column_id !== $done_column_id) { $update_data['date_closed'] = null; } // Обновляем в БД Database::update($this->db_name, $update_data, [ 'id' => $this->id ]); return ['success' => true]; } // Удаление задачи public static function delete($id) { // Проверка что задача существует self::check_task($id); // Удаляем папку с файлами задачи FileUpload::deleteFolder('task', $id); // Удаляем все комментарии задачи и их файлы $comments = Database::select('comments', ['id'], ['id_task' => $id]); if ($comments) { foreach ($comments as $comment) { FileUpload::deleteFolder('comment', $comment['id']); } Database::delete('comments', ['id_task' => $id]); } // Удаляем задачу из базы Database::delete('cards_task', ['id' => $id]); return ['success' => true]; } // === МЕТОДЫ ДЛЯ РАБОТЫ С ФАЙЛАМИ === // Загрузка файла к задаче public static function uploadFile($task_id, $file_base64, $file_name) { // Проверка что задача существует self::check_task($task_id); return FileUpload::upload('task', $task_id, $file_base64, $file_name); } // Удаление файлов задачи public static function deleteFile($task_id, $file_names) { // Проверка что задача существует self::check_task($task_id); return FileUpload::delete('task', $task_id, $file_names); } // Изменение порядка и колонки задачи (с пересчётом order) public static function updateOrder($id, $column_id, $to_index) { // Проверка что задача существует $task = self::check_task($id); $old_column_id = (int)$task['column_id']; $new_column_id = (int)$column_id; $archive = (int)$task['archive']; // Получаем id_ready (колонка "Готово") из проекта $done_column_id = Project::getReadyColumnId($task['id_project']); // Получаем все карточки целевой колонки с тем же статусом архивации (кроме перемещаемой) $cards = Database::select('cards_task', ['id', 'order'], [ 'column_id' => $column_id, 'archive' => $archive, 'id[!]' => $id, 'ORDER' => ['order' => 'ASC'] ]) ?? []; // Вставляем перемещаемую карточку в нужную позицию array_splice($cards, $to_index, 0, [['id' => $id]]); // Пересчитываем order для всех карточек foreach ($cards as $index => $card) { $update_data = [ 'order' => $index, 'column_id' => $column_id ]; // Только для перемещаемой карточки обновляем date_closed if ($card['id'] == $id && $done_column_id) { // Перемещаем В колонку "Готово" — устанавливаем дату закрытия if ($new_column_id === $done_column_id && $old_column_id !== $done_column_id) { $update_data['date_closed'] = date('Y-m-d H:i:s'); } // Перемещаем ИЗ колонки "Готово" — обнуляем дату elseif ($old_column_id === $done_column_id && $new_column_id !== $done_column_id) { $update_data['date_closed'] = null; } } Database::update('cards_task', $update_data, [ 'id' => $card['id'] ]); } return ['success' => true]; } // Получение всех задач проекта // $id_project: ID проекта (обязательный) // $archive: 0 = неархивные, 1 = архивные, null = все public function getAll($id_project, $archive = 0) { $where = [ 'id_project' => $id_project ]; if ($archive !== null) { $where['archive'] = $archive ? 1 : 0; } $tasks = Database::select($this->db_name, [ 'id', 'id_project', 'id_department', 'id_label', 'id_account', 'order', 'column_id', 'date', 'date_create', 'date_closed', 'file_img', 'title', 'descript', 'descript_full', 'archive' ], $where); // Получаем количество комментариев для всех задач одним запросом $task_ids = array_column($tasks, 'id'); $comments_counts = []; if (!empty($task_ids)) { $counts = Database::query( "SELECT id_task, COUNT(*) as cnt FROM comments WHERE id_task IN (" . implode(',', $task_ids) . ") GROUP BY id_task" )->fetchAll(\PDO::FETCH_ASSOC); foreach ($counts as $row) { $comments_counts[$row['id_task']] = (int)$row['cnt']; } } // Декодируем JSON и получаем avatar_url из accounts return array_map(function($task) use ($comments_counts) { $task['file_img'] = $task['file_img'] ? json_decode($task['file_img'], true) : []; // Получаем avatar_url из accounts по id_account if ($task['id_account']) { $account = Database::get('accounts', ['avatar_url'], ['id' => $task['id_account']]); $task['avatar_img'] = $account['avatar_url'] ?? null; } else { $task['avatar_img'] = null; } // Количество комментариев $task['comments_count'] = $comments_counts[$task['id']] ?? 0; return $task; }, $tasks); } // Получение колонок проекта public function getColumns($id_project) { return Database::select('columns', [ 'id', 'name_columns', 'color', 'id_order' ], [ 'id_project' => $id_project, 'ORDER' => ['id_order' => 'ASC'] ]); } // Получение отделов проекта public function getDepartments($id_project) { return Database::select('departments', [ 'id', 'name_departments', 'color' ], [ 'id_project' => $id_project ]); } // Получение всех меток public function getLabels() { return Database::select('labels', [ 'id', 'name_labels', 'icon', 'color' ]); } // Проверка и получение задачи (при ошибке — сразу ответ и exit) public static function check_task($task_id) { $task = Database::get('cards_task', '*', ['id' => $task_id]); if (!$task_id || !$task) { RestApi::response(['success' => false, 'errors' => ['task' => 'Задача не найдена']], 400); } return $task; } // Установка статуса архивации задачи (только для задач в колонке "Готово") public static function setArchive($id, $archive = 1) { // Проверка что задача существует $task = self::check_task($id); // Получаем id_ready (колонка "Готово") из проекта $done_column_id = Project::getReadyColumnId($task['id_project']); // Архивировать можно только задачи в колонке "Готово" if ($archive && $done_column_id && (int)$task['column_id'] !== $done_column_id) { RestApi::response([ 'success' => false, 'errors' => ['column' => 'Архивировать можно только задачи из колонки "Готово"'] ], 400); } // Данные для обновления $update_data = [ 'archive' => $archive ? 1 : 0 ]; // При разархивировании — возвращаем в колонку "Готово" if (!$archive && $done_column_id) { $update_data['column_id'] = $done_column_id; } // Обновляем в БД Database::update('cards_task', $update_data, [ 'id' => $id ]); return ['success' => true, 'archive' => $archive ? 1 : 0]; } } ?>